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.
Files changed (66) hide show
  1. wrfrun/__init__.py +8 -3
  2. wrfrun/cli.py +69 -29
  3. wrfrun/core/__init__.py +27 -10
  4. wrfrun/core/_config.py +308 -0
  5. wrfrun/core/_constant.py +236 -0
  6. wrfrun/core/_exec_db.py +105 -0
  7. wrfrun/core/_namelist.py +287 -0
  8. wrfrun/core/_record.py +178 -0
  9. wrfrun/core/_resource.py +172 -0
  10. wrfrun/core/base.py +132 -406
  11. wrfrun/core/core.py +196 -0
  12. wrfrun/core/error.py +28 -2
  13. wrfrun/core/replay.py +10 -96
  14. wrfrun/core/server.py +52 -27
  15. wrfrun/core/type.py +171 -0
  16. wrfrun/data.py +304 -139
  17. wrfrun/extension/goos_sst/__init__.py +2 -2
  18. wrfrun/extension/goos_sst/core.py +9 -14
  19. wrfrun/extension/goos_sst/res/__init__.py +0 -1
  20. wrfrun/extension/goos_sst/utils.py +50 -44
  21. wrfrun/extension/littler/core.py +105 -88
  22. wrfrun/extension/utils.py +4 -3
  23. wrfrun/log.py +117 -0
  24. wrfrun/model/__init__.py +11 -7
  25. wrfrun/model/constants.py +52 -0
  26. wrfrun/model/palm/__init__.py +30 -0
  27. wrfrun/model/palm/core.py +145 -0
  28. wrfrun/model/palm/namelist.py +33 -0
  29. wrfrun/model/plot.py +99 -119
  30. wrfrun/model/type.py +116 -0
  31. wrfrun/model/utils.py +9 -20
  32. wrfrun/model/wrf/__init__.py +4 -9
  33. wrfrun/model/wrf/core.py +246 -161
  34. wrfrun/model/wrf/exec_wrap.py +13 -12
  35. wrfrun/model/wrf/geodata.py +116 -100
  36. wrfrun/model/wrf/log.py +103 -0
  37. wrfrun/model/wrf/namelist.py +90 -73
  38. wrfrun/model/wrf/plot.py +102 -0
  39. wrfrun/model/wrf/scheme.py +108 -52
  40. wrfrun/model/wrf/utils.py +39 -25
  41. wrfrun/model/wrf/vtable.py +35 -3
  42. wrfrun/plot/__init__.py +20 -0
  43. wrfrun/plot/wps.py +90 -73
  44. wrfrun/res/__init__.py +103 -5
  45. wrfrun/res/config/config.template.toml +8 -0
  46. wrfrun/res/config/palm.template.toml +23 -0
  47. wrfrun/run.py +105 -77
  48. wrfrun/scheduler/__init__.py +1 -0
  49. wrfrun/scheduler/lsf.py +3 -2
  50. wrfrun/scheduler/pbs.py +3 -2
  51. wrfrun/scheduler/script.py +17 -5
  52. wrfrun/scheduler/slurm.py +3 -2
  53. wrfrun/scheduler/utils.py +14 -2
  54. wrfrun/utils.py +88 -199
  55. wrfrun/workspace/__init__.py +8 -5
  56. wrfrun/workspace/core.py +20 -12
  57. wrfrun/workspace/palm.py +137 -0
  58. wrfrun/workspace/wrf.py +16 -15
  59. wrfrun-0.3.0.dist-info/METADATA +240 -0
  60. wrfrun-0.3.0.dist-info/RECORD +78 -0
  61. wrfrun/core/config.py +0 -923
  62. wrfrun/model/base.py +0 -14
  63. wrfrun-0.2.0.dist-info/METADATA +0 -68
  64. wrfrun-0.2.0.dist-info/RECORD +0 -62
  65. {wrfrun-0.2.0.dist-info → wrfrun-0.3.0.dist-info}/WHEEL +0 -0
  66. {wrfrun-0.2.0.dist-info → wrfrun-0.3.0.dist-info}/entry_points.txt +0 -0
@@ -17,7 +17,6 @@ VTABLE_ERA_GOOS_SST
17
17
 
18
18
  from os.path import abspath, dirname
19
19
 
20
-
21
20
  _PATH = abspath(dirname(__file__))
22
21
 
23
22
  VTABLE_ERA_GOOS_SST = f"{_PATH}/Vtable.ERA_GOOS_SST"
@@ -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.utils import logger
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): # type: ignore
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
- "units": "K",
55
- "long_name": "Sea surface temperature",
56
- "standard_name": "Sea surface temperature",
57
- # The following keys and values will be used in GRIB.
58
- "GRIB_paramId": 34,
59
- "GRIB_shortName": "sst",
60
- "GRIB_units": "K",
61
- "GRIB_name": "Sea surface temperature",
62
- "GRIB_stepUnits": 1,
63
- "GRIB_stepType": "instant",
64
- "GRIB_gridType": "regular_ll",
65
- "GRIB_iDirectionIncrementInDegrees": delta_longitude,
66
- "GRIB_iScanNegatively": 0,
67
- "GRIB_jDirectionIncrementInDegrees": delta_latitude,
68
- "GRIB_jScanPositively": 0,
69
- "GRIB_latitudeOfFirstGridPointInDegrees": latitude_start,
70
- "GRIB_latitudeOfLastGridPointInDegrees": latitude_stop,
71
- "GRIB_longitudeOfFirstGridPointInDegrees": longitude_start,
72
- "GRIB_longitudeOfLastGridPointInDegrees": longitude_stop,
73
- "GRIB_Ny": latitude_length,
74
- "GRIB_Nx": longitude_length,
75
- "GRIB_typeOfLevel": "surface",
76
- # The following keys and values can't be found at ECMWF websites.
77
- "GRIB_cfName": "unknown",
78
- "GRIB_cfVarName": "sst",
79
- "GRIB_dataType": "an", # Analysis data, defined at https://codes.ecmwf.int/grib/format/mars/type/
80
- "GRIB_gridDefinitionDescription": "Latitude/Longitude Grid",
81
- # "GRIB_missingValue": -9999,
82
- "GRIB_numberOfPoints": points_number,
83
- "GRIB_totalNumber": 0,
84
- "GRIB_uvRelativeToGird": 0
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
- }, attrs={
91
- "GRIB_centre": "ecmf",
92
- "GRIB_edition": 1,
93
- }), save_path
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
 
@@ -13,15 +13,15 @@ Implementation of ``extension.littler``'s core functionality.
13
13
  LittleR
14
14
  """
15
15
 
16
- from json import loads, dumps
17
- from typing import Union, Tuple, Iterable
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 get_wrfrun_config
24
- from wrfrun.utils import logger
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
- "`length` must be a tuple contain two values `(total length, decimal length)` when `var` is `float`"
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
- "`length` must be an int value when `var` is not `float`"
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, the less the num is, the newer the data is. Defaults to 0.
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 f"{self.latitude:20.5f}" \
224
- f"{self.longitude:20.5f}" \
225
- f"{self.ID.rjust(40, ' ')}" \
226
- f"{self.name.rjust(40, ' ')}" \
227
- f"{self.fm.rjust(40, ' ')}" \
228
- f"{self.source.rjust(40, ' ')}" \
229
- f"{self.elevation:20.5f}" \
230
- f"{self.num_valid_field:10d}" \
231
- f"{self.num_error:10d}" \
232
- f"{self.num_warning:10d}" \
233
- f"{self.num_sequence:10d}" \
234
- f"{self.num_duplicate:10d}" \
235
- f"{to_fstring(self.is_sounding, 10)}" \
236
- f"{to_fstring(self.is_bogus, 10)}" \
237
- f"{to_fstring(self.discard, 10)}" \
238
- f"{self.time:10d}" \
239
- f"{self.julian_day:10d}" \
240
- f"{self.date.rjust(20, ' ')}" + self._generate_data_qc()
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", "pressure_qc",
274
- "height", "height_qc",
275
- "temperature", "temperature_qc",
276
- "dew_point", "dew_point_qc",
277
- "wind_speed", "wind_speed_qc",
278
- "wind_direction", "wind_direction_qc",
279
- "wind_u", "wind_u_qc",
280
- "wind_v", "wind_v_qc",
281
- "relative_humidity", "relative_humidity_qc",
282
- "thickness", "thickness_qc",
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='columns', dtype=None, columns=None):
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 (key, qc_key) in zip(fields, qc_fields):
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 (_, _) in zip(fields, qc_fields):
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, the less the num is, the newer the data is, defaults to 0
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, latitude, fm, elevation, is_bogus, date, ID,
821
- name=name, source=source, num_sequence=num_sequence, sea_level_pressure=sea_level_pressure, reference_pressure=reference_pressure,
822
- surface_pressure=surface_pressure, cloud_cover=cloud_cover, precipitable_water=precipitable_water, quality_control=quality_control,
823
- **kwargs
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 = get_wrfrun_config().parse_resource_uri(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 = get_wrfrun_config().parse_resource_uri(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 get_wrfrun_config
19
- from wrfrun.utils import check_path, logger
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 = get_wrfrun_config()
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"]