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
|
@@ -6,22 +6,21 @@ Functions that are used by :doc:`/api/extension.goos_sst`.
|
|
|
6
6
|
|
|
7
7
|
.. autosummary::
|
|
8
8
|
:toctree: generated/
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
create_sst_grib
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
from cfgrib.xarray_to_grib import to_grib
|
|
14
13
|
from numpy.dtypes import DateTime64DType
|
|
15
14
|
from pandas import to_datetime
|
|
16
15
|
from xarray import DataArray, Dataset
|
|
17
16
|
|
|
18
|
-
from wrfrun.
|
|
17
|
+
from wrfrun.log import logger
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
def create_sst_grib(data: DataArray, save_path: str):
|
|
22
21
|
"""
|
|
23
22
|
Write SST data to a GRIB file.
|
|
24
|
-
|
|
23
|
+
|
|
25
24
|
This function creates GRIB file using ``cfgrib`` package.
|
|
26
25
|
While GRIB write support is experimental in ``cfgrib``,
|
|
27
26
|
this function may **FAIL TO CREATE GRIB FILE**.
|
|
@@ -31,13 +30,16 @@ def create_sst_grib(data: DataArray, save_path: str):
|
|
|
31
30
|
:param save_path: Output GRIB file path.
|
|
32
31
|
:type save_path: str
|
|
33
32
|
"""
|
|
33
|
+
# lazy import to fix sphinx build error
|
|
34
|
+
from cfgrib.xarray_to_grib import to_grib
|
|
35
|
+
|
|
34
36
|
# check the data's dimensions.
|
|
35
37
|
for _dim in ["time", "longitude", "latitude"]:
|
|
36
38
|
if _dim not in data.dims:
|
|
37
39
|
logger.error(f"Essential dimension '{_dim}' not found in data")
|
|
38
40
|
raise KeyError
|
|
39
41
|
|
|
40
|
-
if not isinstance(data["time"].dtype, DateTime64DType):
|
|
42
|
+
if not isinstance(data["time"].dtype, DateTime64DType): # type: ignore
|
|
41
43
|
data = data.assign_coords(time=to_datetime(data["time"].data))
|
|
42
44
|
|
|
43
45
|
data = data.sortby(data["latitude"], ascending=False)
|
|
@@ -50,47 +52,51 @@ def create_sst_grib(data: DataArray, save_path: str):
|
|
|
50
52
|
latitude_length = data["latitude"].size
|
|
51
53
|
points_number = data.size
|
|
52
54
|
|
|
53
|
-
data = data.assign_attrs(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
55
|
+
data = data.assign_attrs(
|
|
56
|
+
**{
|
|
57
|
+
"units": "K",
|
|
58
|
+
"long_name": "Sea surface temperature",
|
|
59
|
+
"standard_name": "Sea surface temperature",
|
|
60
|
+
# The following keys and values will be used in GRIB.
|
|
61
|
+
"GRIB_paramId": 34,
|
|
62
|
+
"GRIB_shortName": "sst",
|
|
63
|
+
"GRIB_units": "K",
|
|
64
|
+
"GRIB_name": "Sea surface temperature",
|
|
65
|
+
"GRIB_stepUnits": 1,
|
|
66
|
+
"GRIB_stepType": "instant",
|
|
67
|
+
"GRIB_gridType": "regular_ll",
|
|
68
|
+
"GRIB_iDirectionIncrementInDegrees": delta_longitude,
|
|
69
|
+
"GRIB_iScanNegatively": 0,
|
|
70
|
+
"GRIB_jDirectionIncrementInDegrees": delta_latitude,
|
|
71
|
+
"GRIB_jScanPositively": 0,
|
|
72
|
+
"GRIB_latitudeOfFirstGridPointInDegrees": latitude_start,
|
|
73
|
+
"GRIB_latitudeOfLastGridPointInDegrees": latitude_stop,
|
|
74
|
+
"GRIB_longitudeOfFirstGridPointInDegrees": longitude_start,
|
|
75
|
+
"GRIB_longitudeOfLastGridPointInDegrees": longitude_stop,
|
|
76
|
+
"GRIB_Ny": latitude_length,
|
|
77
|
+
"GRIB_Nx": longitude_length,
|
|
78
|
+
"GRIB_typeOfLevel": "surface",
|
|
79
|
+
# The following keys and values can't be found at ECMWF websites.
|
|
80
|
+
"GRIB_cfName": "unknown",
|
|
81
|
+
"GRIB_cfVarName": "sst",
|
|
82
|
+
"GRIB_dataType": "an", # Analysis data, defined at https://codes.ecmwf.int/grib/format/mars/type/
|
|
83
|
+
"GRIB_gridDefinitionDescription": "Latitude/Longitude Grid",
|
|
84
|
+
# "GRIB_missingValue": -9999,
|
|
85
|
+
"GRIB_numberOfPoints": points_number,
|
|
86
|
+
"GRIB_totalNumber": 0,
|
|
87
|
+
"GRIB_uvRelativeToGird": 0,
|
|
88
|
+
}
|
|
89
|
+
)
|
|
86
90
|
|
|
87
91
|
to_grib(
|
|
88
|
-
Dataset(
|
|
89
|
-
"sst": data
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
Dataset(
|
|
93
|
+
{"sst": data},
|
|
94
|
+
attrs={
|
|
95
|
+
"GRIB_centre": "ecmf",
|
|
96
|
+
"GRIB_edition": 1,
|
|
97
|
+
},
|
|
98
|
+
),
|
|
99
|
+
save_path,
|
|
94
100
|
)
|
|
95
101
|
|
|
96
102
|
|
wrfrun/extension/littler/core.py
CHANGED
|
@@ -13,15 +13,15 @@ Implementation of ``extension.littler``'s core functionality.
|
|
|
13
13
|
LittleR
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
from json import
|
|
17
|
-
from typing import
|
|
16
|
+
from json import dumps, loads
|
|
17
|
+
from typing import Iterable, Tuple, Union
|
|
18
18
|
from zipfile import ZipFile
|
|
19
19
|
|
|
20
|
-
from pandas import DataFrame, read_csv
|
|
21
20
|
import numpy as np
|
|
21
|
+
from pandas import DataFrame, read_csv
|
|
22
22
|
|
|
23
|
-
from wrfrun.core import
|
|
24
|
-
from wrfrun.
|
|
23
|
+
from wrfrun.core import WRFRUN
|
|
24
|
+
from wrfrun.log import logger
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def to_fstring(var: Union[int, float, bool, str], length: Union[int, Tuple[int, int]]) -> str:
|
|
@@ -52,23 +52,15 @@ def to_fstring(var: Union[int, float, bool, str], length: Union[int, Tuple[int,
|
|
|
52
52
|
"""
|
|
53
53
|
if isinstance(var, float):
|
|
54
54
|
if not isinstance(length, tuple):
|
|
55
|
-
logger.error(
|
|
56
|
-
|
|
57
|
-
)
|
|
58
|
-
raise ValueError(
|
|
59
|
-
"`length` must be a tuple contain two values `(total length, decimal length)` when `var` is `float`"
|
|
60
|
-
)
|
|
55
|
+
logger.error("`length` must be a tuple contain two values `(total length, decimal length)` when `var` is `float`")
|
|
56
|
+
raise ValueError("`length` must be a tuple contain two values `(total length, decimal length)` when `var` is `float`")
|
|
61
57
|
|
|
62
58
|
res = f"{var:{length[0]}.{length[1]}f}"
|
|
63
59
|
|
|
64
60
|
else:
|
|
65
61
|
if not isinstance(length, int):
|
|
66
|
-
logger.error(
|
|
67
|
-
|
|
68
|
-
)
|
|
69
|
-
raise ValueError(
|
|
70
|
-
"`length` must be an int value when `var` is not `float`"
|
|
71
|
-
)
|
|
62
|
+
logger.error("`length` must be an int value when `var` is not `float`")
|
|
63
|
+
raise ValueError("`length` must be an int value when `var` is not `float`")
|
|
72
64
|
|
|
73
65
|
if isinstance(var, bool):
|
|
74
66
|
res = "T" if var else "F"
|
|
@@ -84,6 +76,7 @@ class LittleRHead(dict):
|
|
|
84
76
|
"""
|
|
85
77
|
Headers of LITTLE_R observation data.
|
|
86
78
|
"""
|
|
79
|
+
|
|
87
80
|
def __init__(
|
|
88
81
|
self,
|
|
89
82
|
longitude: float,
|
|
@@ -102,7 +95,7 @@ class LittleRHead(dict):
|
|
|
102
95
|
cloud_cover=-888888,
|
|
103
96
|
precipitable_water=-888888,
|
|
104
97
|
quality_control: Union[dict, int] = 0,
|
|
105
|
-
**kwargs
|
|
98
|
+
**kwargs,
|
|
106
99
|
) -> None:
|
|
107
100
|
"""
|
|
108
101
|
Headers of LITTLE_R observation data.
|
|
@@ -125,7 +118,8 @@ class LittleRHead(dict):
|
|
|
125
118
|
:type name: str
|
|
126
119
|
:param source: Data source, defaults to "Created by wrfrun package".
|
|
127
120
|
:type source: str
|
|
128
|
-
:param num_sequence: Sequence number which is used to merge multiple record data,
|
|
121
|
+
:param num_sequence: Sequence number which is used to merge multiple record data,
|
|
122
|
+
the less the num is, the newer the data is. Defaults to 0.
|
|
129
123
|
:type num_sequence: int
|
|
130
124
|
:param sea_level_pressure: Sea level pressure. Defaults to -888,888.
|
|
131
125
|
:type sea_level_pressure: int
|
|
@@ -220,24 +214,26 @@ class LittleRHead(dict):
|
|
|
220
214
|
# return self._convert_to_fstring()
|
|
221
215
|
|
|
222
216
|
def _convert_to_fstring(self) -> str:
|
|
223
|
-
return
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
217
|
+
return (
|
|
218
|
+
f"{self.latitude:20.5f}"
|
|
219
|
+
f"{self.longitude:20.5f}"
|
|
220
|
+
f"{self.ID.rjust(40, ' ')}"
|
|
221
|
+
f"{self.name.rjust(40, ' ')}"
|
|
222
|
+
f"{self.fm.rjust(40, ' ')}"
|
|
223
|
+
f"{self.source.rjust(40, ' ')}"
|
|
224
|
+
f"{self.elevation:20.5f}"
|
|
225
|
+
f"{self.num_valid_field:10d}"
|
|
226
|
+
f"{self.num_error:10d}"
|
|
227
|
+
f"{self.num_warning:10d}"
|
|
228
|
+
f"{self.num_sequence:10d}"
|
|
229
|
+
f"{self.num_duplicate:10d}"
|
|
230
|
+
f"{to_fstring(self.is_sounding, 10)}"
|
|
231
|
+
f"{to_fstring(self.is_bogus, 10)}"
|
|
232
|
+
f"{to_fstring(self.discard, 10)}"
|
|
233
|
+
f"{self.time:10d}"
|
|
234
|
+
f"{self.julian_day:10d}"
|
|
235
|
+
f"{self.date.rjust(20, ' ')}" + self._generate_data_qc()
|
|
236
|
+
)
|
|
241
237
|
|
|
242
238
|
def _generate_data_qc(self) -> str:
|
|
243
239
|
field = [
|
|
@@ -270,16 +266,26 @@ class LittleRHead(dict):
|
|
|
270
266
|
|
|
271
267
|
|
|
272
268
|
LITTLE_R_DATA_FIELD = [
|
|
273
|
-
"pressure",
|
|
274
|
-
"
|
|
275
|
-
"
|
|
276
|
-
"
|
|
277
|
-
"
|
|
278
|
-
"
|
|
279
|
-
"
|
|
280
|
-
"
|
|
281
|
-
"
|
|
282
|
-
"
|
|
269
|
+
"pressure",
|
|
270
|
+
"pressure_qc",
|
|
271
|
+
"height",
|
|
272
|
+
"height_qc",
|
|
273
|
+
"temperature",
|
|
274
|
+
"temperature_qc",
|
|
275
|
+
"dew_point",
|
|
276
|
+
"dew_point_qc",
|
|
277
|
+
"wind_speed",
|
|
278
|
+
"wind_speed_qc",
|
|
279
|
+
"wind_direction",
|
|
280
|
+
"wind_direction_qc",
|
|
281
|
+
"wind_u",
|
|
282
|
+
"wind_u_qc",
|
|
283
|
+
"wind_v",
|
|
284
|
+
"wind_v_qc",
|
|
285
|
+
"relative_humidity",
|
|
286
|
+
"relative_humidity_qc",
|
|
287
|
+
"thickness",
|
|
288
|
+
"thickness_qc",
|
|
283
289
|
]
|
|
284
290
|
|
|
285
291
|
|
|
@@ -287,23 +293,24 @@ class LittleRData(DataFrame):
|
|
|
287
293
|
"""
|
|
288
294
|
LITTLE_R observation data without headers.
|
|
289
295
|
"""
|
|
296
|
+
|
|
290
297
|
def __init__(
|
|
291
298
|
self,
|
|
292
299
|
data=None,
|
|
293
300
|
index=None,
|
|
294
301
|
columns=None,
|
|
295
|
-
pressure: Union[Iterable, float] = 100000
|
|
296
|
-
height: Union[Iterable, float] = -888888
|
|
297
|
-
temperature: Union[Iterable, float] = 264
|
|
298
|
-
dew_point: Union[Iterable, float] = 263
|
|
299
|
-
wind_speed: Union[Iterable, float] = -888888
|
|
300
|
-
wind_direction: Union[Iterable, float] = -888888
|
|
301
|
-
wind_u: Union[Iterable, float] = -888888
|
|
302
|
-
wind_v: Union[Iterable, float] = -888888
|
|
303
|
-
relative_humidity: Union[Iterable, float] = -888888
|
|
304
|
-
thickness: Union[Iterable, float] = -888888
|
|
302
|
+
pressure: Union[Iterable, float] = 100000.0,
|
|
303
|
+
height: Union[Iterable, float] = -888888.0,
|
|
304
|
+
temperature: Union[Iterable, float] = 264.0,
|
|
305
|
+
dew_point: Union[Iterable, float] = 263.0,
|
|
306
|
+
wind_speed: Union[Iterable, float] = -888888.0,
|
|
307
|
+
wind_direction: Union[Iterable, float] = -888888.0,
|
|
308
|
+
wind_u: Union[Iterable, float] = -888888.0,
|
|
309
|
+
wind_v: Union[Iterable, float] = -888888.0,
|
|
310
|
+
relative_humidity: Union[Iterable, float] = -888888.0,
|
|
311
|
+
thickness: Union[Iterable, float] = -888888.0,
|
|
305
312
|
quality_control_flag: Union[Iterable, dict, int] = 0,
|
|
306
|
-
**kwargs
|
|
313
|
+
**kwargs,
|
|
307
314
|
) -> None:
|
|
308
315
|
"""
|
|
309
316
|
LITTLE_R observation data without headers.
|
|
@@ -411,9 +418,7 @@ class LittleRData(DataFrame):
|
|
|
411
418
|
wind_v = np.array([wind_v]).astype(float)
|
|
412
419
|
relative_humidity = np.array([relative_humidity]).astype(float)
|
|
413
420
|
thickness = np.array([thickness]).astype(float)
|
|
414
|
-
quality_control_flag = np.array(
|
|
415
|
-
[quality_control_flag]
|
|
416
|
-
).astype(int)
|
|
421
|
+
quality_control_flag = np.array([quality_control_flag]).astype(int)
|
|
417
422
|
else:
|
|
418
423
|
pressure = np.asarray(pressure).astype(float)
|
|
419
424
|
height = np.asarray(height).astype(float)
|
|
@@ -425,9 +430,7 @@ class LittleRData(DataFrame):
|
|
|
425
430
|
wind_v = np.asarray(wind_v).astype(float)
|
|
426
431
|
relative_humidity = np.asarray(relative_humidity).astype(float)
|
|
427
432
|
thickness = np.asarray(thickness).astype(float)
|
|
428
|
-
quality_control_flag = np.asarray(
|
|
429
|
-
quality_control_flag
|
|
430
|
-
).astype(int)
|
|
433
|
+
quality_control_flag = np.asarray(quality_control_flag).astype(int)
|
|
431
434
|
|
|
432
435
|
# construct data
|
|
433
436
|
if isinstance(quality_control_flag, dict):
|
|
@@ -493,7 +496,7 @@ class LittleRData(DataFrame):
|
|
|
493
496
|
return cls.from_dict(data_dict)
|
|
494
497
|
|
|
495
498
|
@classmethod
|
|
496
|
-
def from_dict(cls, data: dict, orient=
|
|
499
|
+
def from_dict(cls, data: dict, orient="columns", dtype=None, columns=None):
|
|
497
500
|
"""
|
|
498
501
|
Create ``LittleRData`` instance from a dict.
|
|
499
502
|
This method inspects all fields in ``data`` and supplements any missing fields with invalid value (-888888).
|
|
@@ -517,7 +520,7 @@ class LittleRData(DataFrame):
|
|
|
517
520
|
if field.endswith("_qc"):
|
|
518
521
|
data[field] = np.zeros_like(temp_data).astype(int)
|
|
519
522
|
else:
|
|
520
|
-
data[field] = np.zeros_like(temp_data) - 888888.
|
|
523
|
+
data[field] = np.zeros_like(temp_data) - 888888.0
|
|
521
524
|
|
|
522
525
|
return super().from_dict(data, orient, dtype, columns) # type: ignore
|
|
523
526
|
|
|
@@ -553,7 +556,7 @@ class LittleRData(DataFrame):
|
|
|
553
556
|
res = ""
|
|
554
557
|
valid_field_num = 0
|
|
555
558
|
for row in self.index:
|
|
556
|
-
for
|
|
559
|
+
for key, qc_key in zip(fields, qc_fields):
|
|
557
560
|
_field = self.loc[row, key]
|
|
558
561
|
_field_qc = self.loc[row, qc_key]
|
|
559
562
|
|
|
@@ -564,7 +567,7 @@ class LittleRData(DataFrame):
|
|
|
564
567
|
res += "\n"
|
|
565
568
|
|
|
566
569
|
# add ending record
|
|
567
|
-
for
|
|
570
|
+
for _, _ in zip(fields, qc_fields):
|
|
568
571
|
res += f"{-777777:13.5f}{0:7d}"
|
|
569
572
|
res += "\n"
|
|
570
573
|
|
|
@@ -587,18 +590,18 @@ class LittleR(LittleRData):
|
|
|
587
590
|
index=None,
|
|
588
591
|
columns=None,
|
|
589
592
|
data_header: Union[dict, None] = None,
|
|
590
|
-
pressure: Union[Iterable, float] = 100000
|
|
591
|
-
height: Union[Iterable, float] = -888888
|
|
592
|
-
temperature: Union[Iterable, float] = 264
|
|
593
|
-
dew_point: Union[Iterable, float] = 263
|
|
594
|
-
wind_speed: Union[Iterable, float] = -888888
|
|
595
|
-
wind_direction: Union[Iterable, float] = -888888
|
|
596
|
-
wind_u: Union[Iterable, float] = -888888
|
|
597
|
-
wind_v: Union[Iterable, float] = -888888
|
|
598
|
-
relative_humidity: Union[Iterable, float] = -888888
|
|
599
|
-
thickness: Union[Iterable, float] = -888888
|
|
593
|
+
pressure: Union[Iterable, float] = 100000.0,
|
|
594
|
+
height: Union[Iterable, float] = -888888.0,
|
|
595
|
+
temperature: Union[Iterable, float] = 264.0,
|
|
596
|
+
dew_point: Union[Iterable, float] = 263.0,
|
|
597
|
+
wind_speed: Union[Iterable, float] = -888888.0,
|
|
598
|
+
wind_direction: Union[Iterable, float] = -888888.0,
|
|
599
|
+
wind_u: Union[Iterable, float] = -888888.0,
|
|
600
|
+
wind_v: Union[Iterable, float] = -888888.0,
|
|
601
|
+
relative_humidity: Union[Iterable, float] = -888888.0,
|
|
602
|
+
thickness: Union[Iterable, float] = -888888.0,
|
|
600
603
|
quality_control_flag: Union[Iterable, dict, int] = 0,
|
|
601
|
-
**kwargs
|
|
604
|
+
**kwargs,
|
|
602
605
|
) -> None:
|
|
603
606
|
"""
|
|
604
607
|
``LittleR`` class helps you manage LITTLE_R data easily.
|
|
@@ -751,7 +754,7 @@ class LittleR(LittleRData):
|
|
|
751
754
|
quality_control_flag=quality_control_flag,
|
|
752
755
|
index=index,
|
|
753
756
|
columns=columns,
|
|
754
|
-
**kwargs
|
|
757
|
+
**kwargs,
|
|
755
758
|
)
|
|
756
759
|
|
|
757
760
|
if data_header is None:
|
|
@@ -777,7 +780,7 @@ class LittleR(LittleRData):
|
|
|
777
780
|
cloud_cover=-888888,
|
|
778
781
|
precipitable_water=-888888,
|
|
779
782
|
quality_control: Union[dict, int] = 0,
|
|
780
|
-
**kwargs
|
|
783
|
+
**kwargs,
|
|
781
784
|
):
|
|
782
785
|
"""
|
|
783
786
|
Set headers of LITTLE_R observation data.
|
|
@@ -801,7 +804,8 @@ class LittleR(LittleRData):
|
|
|
801
804
|
:type name: str, optional
|
|
802
805
|
:param source: Data source, defaults to "Created by wrfrun package".
|
|
803
806
|
:type source: str, optional
|
|
804
|
-
:param num_sequence: Sequence number, used to merge multiple record data,
|
|
807
|
+
:param num_sequence: Sequence number, used to merge multiple record data,
|
|
808
|
+
the less the num is, the newer the data is, defaults to 0
|
|
805
809
|
:type num_sequence: int, optional
|
|
806
810
|
:param sea_level_pressure: Sea level pressure, defaults to -888888
|
|
807
811
|
:type sea_level_pressure: int, optional
|
|
@@ -817,10 +821,23 @@ class LittleR(LittleRData):
|
|
|
817
821
|
:type quality_control: Union[dict, int], optional
|
|
818
822
|
"""
|
|
819
823
|
self.little_r_head = LittleRHead(
|
|
820
|
-
longitude,
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
+
longitude,
|
|
825
|
+
latitude,
|
|
826
|
+
fm,
|
|
827
|
+
elevation,
|
|
828
|
+
is_bogus,
|
|
829
|
+
date,
|
|
830
|
+
ID,
|
|
831
|
+
name=name,
|
|
832
|
+
source=source,
|
|
833
|
+
num_sequence=num_sequence,
|
|
834
|
+
sea_level_pressure=sea_level_pressure,
|
|
835
|
+
reference_pressure=reference_pressure,
|
|
836
|
+
surface_pressure=surface_pressure,
|
|
837
|
+
cloud_cover=cloud_cover,
|
|
838
|
+
precipitable_water=precipitable_water,
|
|
839
|
+
quality_control=quality_control,
|
|
840
|
+
**kwargs,
|
|
824
841
|
)
|
|
825
842
|
|
|
826
843
|
def __str__(self) -> str:
|
|
@@ -849,7 +866,7 @@ class LittleR(LittleRData):
|
|
|
849
866
|
if not file_path.endswith(".zlr"):
|
|
850
867
|
file_path = f"{file_path}.zlr"
|
|
851
868
|
|
|
852
|
-
file_path =
|
|
869
|
+
file_path = WRFRUN.config.parse_resource_uri(file_path)
|
|
853
870
|
|
|
854
871
|
with ZipFile(file_path, "w") as zip_file:
|
|
855
872
|
with zip_file.open("header", "w") as header_file:
|
|
@@ -868,7 +885,7 @@ class LittleR(LittleRData):
|
|
|
868
885
|
:return: ``LittleR`` instance.
|
|
869
886
|
:rtype: LittleR
|
|
870
887
|
"""
|
|
871
|
-
file_path =
|
|
888
|
+
file_path = WRFRUN.config.parse_resource_uri(file_path)
|
|
872
889
|
|
|
873
890
|
with ZipFile(file_path, "r") as zip_file:
|
|
874
891
|
with zip_file.open("header", "r") as header_file:
|
wrfrun/extension/utils.py
CHANGED
|
@@ -15,8 +15,9 @@ from os.path import exists
|
|
|
15
15
|
from shutil import copyfile
|
|
16
16
|
from typing import List, Optional
|
|
17
17
|
|
|
18
|
-
from wrfrun.core import
|
|
19
|
-
from wrfrun.
|
|
18
|
+
from wrfrun.core import WRFRUN
|
|
19
|
+
from wrfrun.log import logger
|
|
20
|
+
from wrfrun.utils import check_path
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def extension_postprocess(output_dir: str, extension_id: str, outputs: Optional[List[str]] = None):
|
|
@@ -37,7 +38,7 @@ def extension_postprocess(output_dir: str, extension_id: str, outputs: Optional[
|
|
|
37
38
|
:param outputs: A list contains multiple filenames. Files in this will be treated as outputs.
|
|
38
39
|
:type outputs: list
|
|
39
40
|
"""
|
|
40
|
-
WRFRUNConfig =
|
|
41
|
+
WRFRUNConfig = WRFRUN.config
|
|
41
42
|
output_path = WRFRUNConfig.WRFRUN_OUTPUT_PATH
|
|
42
43
|
output_save_path = f"{output_path}/{extension_id}"
|
|
43
44
|
log_save_path = f"{output_path}/{extension_id}/logs"
|
wrfrun/log.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""
|
|
2
|
+
wrfrun.log
|
|
3
|
+
##########
|
|
4
|
+
|
|
5
|
+
.. autosummary::
|
|
6
|
+
:toctree: generated/
|
|
7
|
+
|
|
8
|
+
get_wrfrun_rich_console
|
|
9
|
+
logger_add_file_handler
|
|
10
|
+
set_logger
|
|
11
|
+
unify_logger_format
|
|
12
|
+
|
|
13
|
+
``wrfrun`` submodule to manage logs. This submodule only take care of ``wrfrun``'s log.
|
|
14
|
+
|
|
15
|
+
For functions and classes which parse numerical models' log, please check :doc:`model </api/model>`.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import logging
|
|
19
|
+
from datetime import datetime
|
|
20
|
+
from os import environ
|
|
21
|
+
from time import time
|
|
22
|
+
from typing import Dict, List, Optional
|
|
23
|
+
|
|
24
|
+
from rich.console import Console
|
|
25
|
+
from rich.logging import RichHandler
|
|
26
|
+
|
|
27
|
+
from .utils import check_path
|
|
28
|
+
|
|
29
|
+
WRFRUN_RICH_CONSOLE = Console()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def set_logger(logger_list: List[str], logger_level: Optional[Dict] = None):
|
|
33
|
+
"""
|
|
34
|
+
This function will replace all handlers of each logger in ``logger_list`` with RichHandler.
|
|
35
|
+
|
|
36
|
+
If there are some custom handlers in logger, they will be replaced too.
|
|
37
|
+
|
|
38
|
+
:param logger_list: A list contains loggers.
|
|
39
|
+
:type logger_list: list
|
|
40
|
+
:param logger_level: You can specify the log level in ``logger_level``, with the name of logger is the key,
|
|
41
|
+
and the level of logger is the value.
|
|
42
|
+
Default if None, with which all loggers' level will be set to ``logging.WARNING``.
|
|
43
|
+
:type logger_level: list | None
|
|
44
|
+
"""
|
|
45
|
+
formatter = logging.Formatter("%(name)s :: %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
|
|
46
|
+
# use rich handler
|
|
47
|
+
handler = RichHandler(console=WRFRUN_RICH_CONSOLE)
|
|
48
|
+
handler.setFormatter(formatter)
|
|
49
|
+
|
|
50
|
+
for logger_name in logger_list:
|
|
51
|
+
if logger_name in logging.root.manager.loggerDict:
|
|
52
|
+
_logger = logging.getLogger(logger_name)
|
|
53
|
+
for _handler in _logger.handlers:
|
|
54
|
+
_logger.removeHandler(_handler)
|
|
55
|
+
_logger.addHandler(handler)
|
|
56
|
+
|
|
57
|
+
if logger_level is not None and logger_name in logger_level:
|
|
58
|
+
_logger.setLevel(logger_level[logger_name])
|
|
59
|
+
else:
|
|
60
|
+
_logger.setLevel(logging.WARNING)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def unify_logger_format():
|
|
64
|
+
"""
|
|
65
|
+
This function is only supposed to be used internally.
|
|
66
|
+
|
|
67
|
+
This function will replace all handlers of each logger with ``rich.logging.RichHandler``.
|
|
68
|
+
Use this carefully.
|
|
69
|
+
"""
|
|
70
|
+
set_logger(
|
|
71
|
+
["cdsapi", "cfgrib", "datapi"],
|
|
72
|
+
{"cdsapi": logging.INFO, "cfgrib": logging.ERROR, "datapi": logging.INFO},
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# init wrfrun logger
|
|
77
|
+
logger = logging.getLogger("wrfrun")
|
|
78
|
+
# check environment variables and set logger level
|
|
79
|
+
if "WRFRUN_DEBUG_MODE" in environ and environ["WRFRUN_DEBUG_MODE"]:
|
|
80
|
+
_logger_level = logging.DEBUG
|
|
81
|
+
_debug_mode = True
|
|
82
|
+
else:
|
|
83
|
+
_logger_level = logging.INFO
|
|
84
|
+
_debug_mode = False
|
|
85
|
+
set_logger(["wrfrun"], {"wrfrun": _logger_level})
|
|
86
|
+
logger.debug(f"DEBUG MODE: {_debug_mode}")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def logger_add_file_handler(log_path: str):
|
|
90
|
+
"""
|
|
91
|
+
Set log file of logger.
|
|
92
|
+
|
|
93
|
+
:param log_path: Log file path.
|
|
94
|
+
:type log_path: str
|
|
95
|
+
"""
|
|
96
|
+
# check log save path
|
|
97
|
+
check_path(log_path)
|
|
98
|
+
|
|
99
|
+
# add file handler
|
|
100
|
+
file_handler = logging.FileHandler(f"{log_path}/{datetime.fromtimestamp(time()).strftime('%Y-%m-%d %H:%M:%S')}.log")
|
|
101
|
+
file_handler.setFormatter(
|
|
102
|
+
logging.Formatter("%(asctime)s - %(name)s - %(levelname)s :: %(message)s", datefmt="%m-%d %H:%M:%S")
|
|
103
|
+
)
|
|
104
|
+
logger.addHandler(file_handler)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_wrfrun_rich_console() -> Console:
|
|
108
|
+
"""
|
|
109
|
+
Get ``rich.console.Console`` instance used in wrfrun.
|
|
110
|
+
|
|
111
|
+
:return: Console instance.
|
|
112
|
+
:rtype: Console
|
|
113
|
+
"""
|
|
114
|
+
return WRFRUN_RICH_CONSOLE
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
__all__ = ["set_logger", "unify_logger_format", "logger_add_file_handler", "logger", "get_wrfrun_rich_console"]
|