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
wrfrun/data.py ADDED
@@ -0,0 +1,418 @@
1
+ from datetime import datetime
2
+ from os import makedirs
3
+ from os.path import exists, dirname
4
+ from typing import Union, List, Tuple
5
+
6
+ from pandas import date_range
7
+ # from seafog import goos_sst_find_data
8
+
9
+ import cdsapi
10
+
11
+ from .core.config import WRFRUNConfig
12
+ from .utils import logger
13
+
14
+ CDS_CLIENT = cdsapi.Client()
15
+
16
+
17
+ class ERA5CONFIG:
18
+ """
19
+ A class to store parameters we will use to download ERA5 data from cdsapi.
20
+ """
21
+
22
+ # dataset name
23
+ DATASET_ERA5_SINGLE_LEVEL = "reanalysis-era5-single-levels"
24
+ DATASET_ERA5_PRESSURE_LEVEL = "reanalysis-era5-pressure-levels"
25
+
26
+ # type name
27
+ TYPE_REANALYSIS = "reanalysis"
28
+
29
+ # format name
30
+ FORMAT_NETCDF = "netcdf"
31
+ FORMAT_GRIB = "grib"
32
+
33
+ # download format
34
+ DOWNLOAD_ZIP = "zip"
35
+ DOWNLOAD_UNZIP = "unarchived"
36
+
37
+ # all level
38
+ PRESSURE_LEVEL = [
39
+ '1', '2', '3',
40
+ '5', '7', '10',
41
+ '20', '30', '50',
42
+ '70', '100', '125',
43
+ '150', '175', '200',
44
+ '225', '250', '300',
45
+ '350', '400', '450',
46
+ '500', '550', '600',
47
+ '650', '700', '750',
48
+ '775', '800', '825',
49
+ '850', '875', '900',
50
+ '925', '950', '975',
51
+ '1000',
52
+ ]
53
+
54
+ # variable name
55
+ VARIABLE_2M_TEMPERATURE = "2m_temperature"
56
+ VARIABLE_2M_DEWPOINT_TEMP = "2m_dewpoint_temperature"
57
+ VARIABLE_LANDSEA_MASK = "land_sea_mask"
58
+ VARIABLE_MEAN_SEA_LEVEL_PRESSURE = "mean_sea_level_pressure"
59
+ VARIABLE_SKIN_TEMPERATURE = "skin_temperature"
60
+ VARIABLE_SNOW_DENSITY = "snow_density"
61
+ VARIABLE_SNOW_DEPTH = "snow_depth"
62
+ VARIABLE_SOIL_TEMP_LEVEL_1 = "soil_temperature_level_1"
63
+ VARIABLE_SOIL_TEMP_LEVEL_2 = "soil_temperature_level_2"
64
+ VARIABLE_SOIL_TEMP_LEVEL_3 = "soil_temperature_level_3"
65
+ VARIABLE_SOIL_TEMP_LEVEL_4 = "soil_temperature_level_4"
66
+ VARIABLE_SURFACE_PRESSURE = "surface_pressure"
67
+ VARIABLE_VOLUMETRIC_SOIL_WATER_LAYER_1 = "volumetric_soil_water_layer_1"
68
+ VARIABLE_VOLUMETRIC_SOIL_WATER_LAYER_2 = "volumetric_soil_water_layer_2"
69
+ VARIABLE_VOLUMETRIC_SOIL_WATER_LAYER_3 = "volumetric_soil_water_layer_3"
70
+ VARIABLE_VOLUMETRIC_SOIL_WATER_LAYER_4 = "volumetric_soil_water_layer_4"
71
+ VARIABLE_10M_U_WIND = "10m_u_component_of_wind"
72
+ VARIABLE_10M_V_WIND = "10m_v_component_of_wind"
73
+ VARIABLE_U_WIND = "u_component_of_wind"
74
+ VARIABLE_V_WIND = "v_component_of_wind"
75
+ VARIABLE_SPECIFIC_HUMIDITY = "specific_humidity"
76
+ VARIABLE_RELATIVE_HUMIDITY = "relative_humidity"
77
+ VARIABLE_GEOPOTENTIAL = "geopotential"
78
+ VARIABLE_TEMPERATURE = "temperature"
79
+
80
+ # name in downloaded data
81
+ NAME_MAP = {
82
+ "2m_temperature": "t2m",
83
+ "10m_u_component_of_wind": "u10",
84
+ "u_component_of_wind": "u",
85
+ "v_component_of_wind": "v",
86
+ "10m_v_component_of_wind": "v10",
87
+ "specific_humidity": "q",
88
+ "geopotential": "z",
89
+ "relative_humidity": "r",
90
+ "temperature": "t",
91
+ "2m_dewpoint_temperature": "d2m",
92
+ "land_sea_mask": "lsm",
93
+ "mean_sea_level_pressure": "msl",
94
+ "skin_temperature": "skt",
95
+ "snow_density": "rsn",
96
+ "snow_depth": "sd",
97
+ "soil_temperature_level_1": "stl1",
98
+ "soil_temperature_level_2": "stl2",
99
+ "soil_temperature_level_3": "stl3",
100
+ "soil_temperature_level_4": "stl4",
101
+ "surface_pressure": "sp",
102
+ "volumetric_soil_water_layer_1": "swvl1",
103
+ "volumetric_soil_water_layer_2": "swvl2",
104
+ "volumetric_soil_water_layer_3": "swvl3",
105
+ "volumetric_soil_water_layer_4": "swvl4"
106
+ }
107
+
108
+ # use a dict to distinguish between two types of data
109
+ TYPE_MAP = {
110
+ "reanalysis-era5-single-levels": (
111
+ "2m_temperature",
112
+ "2m_dewpoint_temperature",
113
+ "land_sea_mask",
114
+ "skin_temperature",
115
+ "snow_density",
116
+ "snow_depth",
117
+ "mean_sea_level_pressure",
118
+ "10m_u_component_of_wind",
119
+ "10m_v_component_of_wind",
120
+ "soil_temperature_level_1",
121
+ "soil_temperature_level_2",
122
+ "soil_temperature_level_3",
123
+ "soil_temperature_level_4",
124
+ "surface_pressure",
125
+ "volumetric_soil_water_layer_1",
126
+ "volumetric_soil_water_layer_2",
127
+ "volumetric_soil_water_layer_3",
128
+ "volumetric_soil_water_layer_4"
129
+
130
+ ),
131
+ "reanalysis-era5-pressure-levels": (
132
+ "specific_humidity",
133
+ "u_component_of_wind",
134
+ "v_component_of_wind",
135
+ "geopotential",
136
+ "relative_humidity",
137
+ "temperature"
138
+ )
139
+ }
140
+
141
+
142
+ def _check_variables_and_datasets(variables: Union[str, Tuple[str, ...]], dataset: str) -> bool:
143
+ """Check if variables and datasets correspond
144
+
145
+ Args:
146
+ variables (str | tuple[str]): Variables type
147
+ dataset (str): Dataset type
148
+
149
+ Returns:
150
+ bool: If check passed, return True, else False
151
+ """
152
+ if isinstance(variables, str):
153
+ if variables in ERA5CONFIG.TYPE_MAP[dataset]:
154
+ return True
155
+ else:
156
+ return False
157
+ else:
158
+ for variable in variables:
159
+ if variable not in ERA5CONFIG.TYPE_MAP[dataset]:
160
+ return False
161
+
162
+ return True
163
+
164
+
165
+ def _check_pressure_level(pressure_level: Union[str, List[str]]) -> bool:
166
+ """Check pressure level
167
+
168
+ Args:
169
+ pressure_level (int | list[int]): A integer value or a list contains pressure values
170
+
171
+ Returns:
172
+ bool: If check passed, return True, else False
173
+ """
174
+ valid_pressure_level = [
175
+ '1', '2', '3',
176
+ '5', '7', '10',
177
+ '20', '30', '50',
178
+ '70', '100', '125',
179
+ '150', '175', '200',
180
+ '225', '250', '300',
181
+ '350', '400', '450',
182
+ '500', '550', '600',
183
+ '650', '700', '750',
184
+ '775', '800', '825',
185
+ '850', '875', '900',
186
+ '925', '950', '975',
187
+ '1000',
188
+ ]
189
+
190
+ for level in pressure_level:
191
+ if level not in valid_pressure_level:
192
+ return False
193
+
194
+ return True
195
+
196
+
197
+ def find_era5_data(date: Union[List[str], List[datetime]], area: Tuple[int, int, int, int], variables: Union[Tuple[str, ...], str], save_path: str,
198
+ product_type=ERA5CONFIG.TYPE_REANALYSIS, data_format=ERA5CONFIG.FORMAT_NETCDF, dataset=ERA5CONFIG.DATASET_ERA5_SINGLE_LEVEL,
199
+ download_format=ERA5CONFIG.DOWNLOAD_UNZIP, pressure_level: Union[int, List[int], str, List[str], None] = None, overwrite=False) -> str:
200
+ """
201
+ download era5 data
202
+ Args:
203
+ date: data date, string (for example, `2020-03-25 00:00`) or datetime object, UTC time
204
+ area: range of longitude and latitude, `[lon1, lon2, lat1, lat2]`
205
+ variables: variables, tuple of str or single string
206
+ save_path: save file path
207
+ product_type: product type, default is reanalysis
208
+ data_format: data format, default is netcdf
209
+ dataset: dataset type, default is reanalysis-era5-single-levels
210
+ download_format: zip or unarchived.
211
+ pressure_level: pressure levels.
212
+ overwrite (bool): If the data file exists, force to download it when `overwrite=True`
213
+
214
+ Returns: data path
215
+
216
+ """
217
+ # check variables and datasets
218
+ if not _check_variables_and_datasets(variables, dataset):
219
+ logger.error(
220
+ f"Variables {variables} and dataset {dataset} doesn't correspond, check it")
221
+ exit(1)
222
+
223
+ # check if we need to create directory
224
+ save_folder = dirname(save_path)
225
+ if not exists(save_folder):
226
+ makedirs(save_folder)
227
+
228
+ # re-generate area tuple
229
+ area = (area[-1], area[0], area[-2], area[1])
230
+
231
+ # parse date
232
+ if isinstance(date[0], str):
233
+ date = [datetime.strptime(_date, "%Y-%m-%d %H:%M") # type: ignore
234
+ for _date in date]
235
+ year = list(set(_date.strftime("%Y") for _date in date)) # type: ignore
236
+ month = list(set(_date.strftime("%m") for _date in date)) # type: ignore
237
+ day = list(set(_date.strftime("%d") for _date in date)) # type: ignore
238
+ time = list(set(_date.strftime("%H:%M") for _date in date)) # type: ignore
239
+
240
+ # sort list
241
+ year.sort()
242
+ month.sort()
243
+ day.sort()
244
+ time.sort()
245
+
246
+ # check if it exists
247
+ if exists(save_path) and not overwrite:
248
+ return save_path
249
+
250
+ # create params dict
251
+ params_dict = {
252
+ 'product_type': product_type,
253
+ 'data_format': data_format,
254
+ 'download_format': download_format,
255
+ 'variable': variables,
256
+ 'year': year,
257
+ 'month': month,
258
+ 'day': day,
259
+ 'time': time,
260
+ 'area': area,
261
+ }
262
+
263
+ # check if we need to add pressure_level to params dict
264
+ if dataset == ERA5CONFIG.DATASET_ERA5_PRESSURE_LEVEL:
265
+ if pressure_level is None:
266
+ logger.error(
267
+ f"You need to provide pressure levels to download data")
268
+ exit(1)
269
+ # convert value to str
270
+ if not isinstance(pressure_level, list):
271
+ pressure_level = [pressure_level] # type: ignore
272
+ if not isinstance(pressure_level[0], str): # type: ignore
273
+ pressure_level = [str(int(x))
274
+ for x in pressure_level] # type: ignore
275
+ # check
276
+ if _check_pressure_level(pressure_level): # type: ignore
277
+ params_dict["pressure_level"] = pressure_level
278
+ else:
279
+ logger.error(
280
+ f"You have passed wrong pressure level to download data, check it")
281
+ exit(1)
282
+
283
+ # download data
284
+ logger.info(
285
+ f"Downloading data to {save_path}, it may take several tens of minutes, please wait...")
286
+ CDS_CLIENT.retrieve(dataset, params_dict, save_path)
287
+
288
+ return save_path
289
+
290
+
291
+ def prepare_wps_input_data(area: Tuple[int, int, int, int]):
292
+ """Download essential data for WPS.
293
+
294
+ Args:
295
+ area (Tuple[int, int, int, int]): Range of longitude and latitude, `[lon1, lon2, lat1, lat2]`.
296
+ """
297
+ wrf_config = WRFRUNConfig.get_model_config("wrf")
298
+ # get start and end date from config
299
+ start_date = wrf_config["time"]["start_date"]
300
+ end_date = wrf_config["time"]["end_date"]
301
+
302
+ # remove second part
303
+ start_date = start_date[:-3]
304
+ end_date = end_date[:-3]
305
+
306
+ # get hour step
307
+ hour_step = wrf_config["time"]["input_data_interval"] // 3600
308
+
309
+ # get data save path
310
+ bg_save_path = wrf_config["wps_input_data_folder"]
311
+ sst_save_path = wrf_config["near_goos_data_folder"]
312
+
313
+ # download data
314
+ logger.info(f"Download background data of surface level...")
315
+ download_data(start_date, end_date, hour_step, area, f"{bg_save_path}/surface.grib",
316
+ data_format="grib", data_type="surface", overwrite=True)
317
+
318
+ logger.info(f"Download background data of pressure level...")
319
+ download_data(start_date, end_date, hour_step, area, f"{bg_save_path}/pressure.grib",
320
+ data_format="grib", data_type="pressure", overwrite=True)
321
+
322
+ # logger.info(f"Download NearGOOS data...")
323
+ # download_data(start_date, end_date, hour_step, area,
324
+ # save_path=sst_save_path, data_type="goos", overwrite=True)
325
+
326
+
327
+ def download_data(
328
+ start_date: str,
329
+ end_date: str,
330
+ hour_step: int,
331
+ area: Tuple[int, int, int, int],
332
+ save_path: str,
333
+ data_format="nc",
334
+ data_type="pressure",
335
+ overwrite=False) -> str:
336
+ """Download essential data
337
+
338
+ Args:
339
+ start_date (str): Begin date, for example, "2022-05-19 12:00"
340
+ end_date (str): End date, for example, "2022-05-22 18:00"
341
+ hour_step (int): Hour step
342
+ area (tuple): Range of longitude and latitude, `[lon1, lon2, lat1, lat2]`
343
+ save_path (str): Data save path (era5 data) or data save folder path (goos sst)
344
+ data_format (str): Download data format, "nc" or "grib". Default is "nc"
345
+ data_type (str): Download data type, "pressure", "surface" or "goos". Default is "pressure".
346
+ overwrite (bool): If the data file exists, force to download it when `overwrite=True`
347
+
348
+ Returns:
349
+ str: Data save path
350
+ """
351
+ # generate date list
352
+ date_list = date_range(
353
+ start_date, end_date, freq=f"{hour_step}H"
354
+ ).strftime("%Y-%m-%d %H:%M").to_list()
355
+
356
+ # check format
357
+ if data_format == "nc":
358
+ data_format = ERA5CONFIG.FORMAT_NETCDF
359
+ elif data_format == "grib":
360
+ data_format = ERA5CONFIG.FORMAT_GRIB
361
+ else:
362
+ logger.error(f"Wrong data format: {data_format}")
363
+ raise KeyError
364
+
365
+ # check data type
366
+ if data_type == "pressure":
367
+ data_type = ERA5CONFIG.DATASET_ERA5_PRESSURE_LEVEL
368
+ variables = (
369
+ ERA5CONFIG.VARIABLE_GEOPOTENTIAL,
370
+ ERA5CONFIG.VARIABLE_RELATIVE_HUMIDITY,
371
+ ERA5CONFIG.VARIABLE_SPECIFIC_HUMIDITY,
372
+ ERA5CONFIG.VARIABLE_TEMPERATURE,
373
+ ERA5CONFIG.VARIABLE_U_WIND,
374
+ ERA5CONFIG.VARIABLE_V_WIND
375
+ )
376
+ pressure_level = ERA5CONFIG.PRESSURE_LEVEL
377
+ elif data_type == "surface":
378
+ data_type = ERA5CONFIG.DATASET_ERA5_SINGLE_LEVEL
379
+ variables = (
380
+ ERA5CONFIG.VARIABLE_SURFACE_PRESSURE,
381
+ ERA5CONFIG.VARIABLE_MEAN_SEA_LEVEL_PRESSURE,
382
+ ERA5CONFIG.VARIABLE_SKIN_TEMPERATURE,
383
+ ERA5CONFIG.VARIABLE_2M_TEMPERATURE,
384
+ ERA5CONFIG.VARIABLE_2M_DEWPOINT_TEMP,
385
+ ERA5CONFIG.VARIABLE_10M_U_WIND,
386
+ ERA5CONFIG.VARIABLE_10M_V_WIND,
387
+ ERA5CONFIG.VARIABLE_LANDSEA_MASK,
388
+ ERA5CONFIG.VARIABLE_SOIL_TEMP_LEVEL_1,
389
+ ERA5CONFIG.VARIABLE_SOIL_TEMP_LEVEL_2,
390
+ ERA5CONFIG.VARIABLE_SOIL_TEMP_LEVEL_3,
391
+ ERA5CONFIG.VARIABLE_SOIL_TEMP_LEVEL_4,
392
+ ERA5CONFIG.VARIABLE_VOLUMETRIC_SOIL_WATER_LAYER_1,
393
+ ERA5CONFIG.VARIABLE_VOLUMETRIC_SOIL_WATER_LAYER_2,
394
+ ERA5CONFIG.VARIABLE_VOLUMETRIC_SOIL_WATER_LAYER_3,
395
+ ERA5CONFIG.VARIABLE_VOLUMETRIC_SOIL_WATER_LAYER_4,
396
+ ERA5CONFIG.VARIABLE_SNOW_DEPTH,
397
+ ERA5CONFIG.VARIABLE_SNOW_DENSITY
398
+ )
399
+ pressure_level = None
400
+ elif data_type == "goos":
401
+ logger.warning(f"NEAR-GOOS SST data hasn't been supported yet")
402
+ # download sst data
403
+ # for _date in date_list:
404
+ # _ = goos_sst_find_data(_date, save_path=save_path)
405
+
406
+ return ""
407
+ else:
408
+ logger.error(f"Wrong data type: {data_type}")
409
+ raise KeyError
410
+
411
+ # download data
412
+ return find_era5_data(date=date_list, area=area, variables=variables, # type: ignore
413
+ save_path=save_path, data_format=data_format,
414
+ dataset=data_type, pressure_level=pressure_level, # type: ignore
415
+ download_format=ERA5CONFIG.DOWNLOAD_UNZIP, overwrite=overwrite)
416
+
417
+
418
+ __all__ = ["find_era5_data", "ERA5CONFIG", "download_data", "prepare_wps_input_data"]
@@ -0,0 +1 @@
1
+ from .utils import *
@@ -0,0 +1 @@
1
+ from .utils import *