wrfrun 0.2.0__py3-none-any.whl → 0.3.1__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 +96 -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.1.dist-info/METADATA +239 -0
  60. wrfrun-0.3.1.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.1.dist-info}/WHEEL +0 -0
  66. {wrfrun-0.2.0.dist-info → wrfrun-0.3.1.dist-info}/entry_points.txt +0 -0
wrfrun/model/__init__.py CHANGED
@@ -3,12 +3,14 @@ wrfrun.model
3
3
  ############
4
4
 
5
5
  To be able to handle different numerical models,
6
- ``wrfrun`` implements various ``Executable`` for model's binary executable based on :class:`ExecutableBase <wrfrun.core.base.ExecutableBase>`,
6
+ ``wrfrun`` implements various ``Executable`` for model's binary executable
7
+ based on :class:`ExecutableBase <wrfrun.core.base.ExecutableBase>`,
7
8
  which are all placed in this module.
8
9
 
9
10
  ``wrfrun.model`` currently supports following numerical models:
10
11
 
11
12
  ========================================= ==========================================
13
+ :doc:`palm </api/model.palm>` Support for PALM
12
14
  :doc:`wrf </api/model.wrf>` Support for WRF
13
15
  ========================================= ==========================================
14
16
 
@@ -16,15 +18,17 @@ which are all placed in this module.
16
18
  :maxdepth: 1
17
19
  :hidden:
18
20
 
19
- wrf <model.wrf>
20
- base <model.base>
21
+ constants <model.constants>
22
+ palm <model.palm>
21
23
  plot <model.plot>
24
+ type <model.type>
22
25
  utils <model.utils>
26
+ wrf <model.wrf>
23
27
  """
24
28
 
25
- from .base import *
29
+ # just to register executables
30
+ from . import palm as _
31
+ from . import wrf as _
32
+ from .constants import *
26
33
  from .plot import *
27
34
  from .utils import *
28
-
29
- # just to register executables
30
- from . import wrf
@@ -0,0 +1,52 @@
1
+ """
2
+ wrfrun.model.constants
3
+ ######################
4
+
5
+ Definition of constants used by models.
6
+
7
+ .. autosummary::
8
+ :toctree: generated/
9
+
10
+ NamelistName
11
+ """
12
+
13
+ from dataclasses import dataclass
14
+
15
+
16
+ @dataclass
17
+ class NamelistName:
18
+ """
19
+ Namelist file names.
20
+
21
+ .. py:attribute:: WPS
22
+ :type: str
23
+ :value: namelist.wps
24
+
25
+ WPS namelist file name.
26
+
27
+ .. py:attribute:: WRF
28
+ :type: str
29
+ :value: namelist.input
30
+
31
+ WRF namelist file name.
32
+
33
+ .. py:attribute:: WRFDA
34
+ :type: str
35
+ :value: namelist.input
36
+
37
+ WRFDA namelist file name.
38
+
39
+ .. py:attribute:: PALM
40
+ :type: str
41
+ :value: wrfrun_p3d
42
+
43
+ PALM namelist file name.
44
+ """
45
+
46
+ WPS = "namelist.wps"
47
+ WRF = "namelist.input"
48
+ WRFDA = "namelist.input"
49
+ PALM = "wrfrun_p3d"
50
+
51
+
52
+ __all__ = ["NamelistName"]
@@ -0,0 +1,30 @@
1
+ """
2
+ wrfrun.model.palm
3
+ #################
4
+
5
+ Implementation of PALM model.
6
+
7
+ Submodules
8
+ **********
9
+
10
+ ============================================ ==================================================================================
11
+ :doc:`core </api/model.palm.core>` Core implementation of PALM model.
12
+ :doc:`namelist </api/model.palm.namelist>` Functions to process PALM namelist files.
13
+ ============================================ ==================================================================================
14
+
15
+ About the implementation of PALM
16
+ ********************************
17
+
18
+ Since ``PALM`` provides a bash script ``palmrun`` to take care of the running of ``palm``,
19
+ ``wrfrun`` just simply wraps the bash script.
20
+
21
+ .. toctree::
22
+ :maxdepth: 1
23
+ :hidden:
24
+
25
+ core <model.palm.core>
26
+ namelist <model.palm.namelist>
27
+ """
28
+
29
+ from .core import *
30
+ from .namelist import *
@@ -0,0 +1,145 @@
1
+ """
2
+ wrfrun.model.palm.core
3
+ ######################
4
+
5
+ Core implementation of PALM model. All ``Executable`` and function interface of PALM model are defined here.
6
+
7
+ .. autosummary::
8
+ :toctree: generated/
9
+
10
+ _check_and_prepare_namelist
11
+ PALMRun
12
+ palmrun
13
+ """
14
+
15
+ from os.path import exists
16
+ from typing import Optional
17
+
18
+ from wrfrun.core import WRFRUN, ExecutableBase, ExecutableDB
19
+ from wrfrun.log import logger
20
+ from wrfrun.workspace.palm import get_palm_workspace_path
21
+
22
+ from ..constants import NamelistName
23
+ from .namelist import prepare_palm_namelist
24
+
25
+
26
+ def _check_and_prepare_namelist():
27
+ """
28
+ Check if namelist of ``PALM`` has been loaded.
29
+ If not, call :func:`prepare_palm_namelist <wrfrun.model.palm.namelist.prepare_palm_namelist>` to load it.
30
+ """
31
+ if not WRFRUN.config.check_namelist("palm"):
32
+ prepare_palm_namelist()
33
+
34
+
35
+ class PALMRun(ExecutableBase):
36
+ """
37
+ ``Executable`` for bash script "palmrun".
38
+ """
39
+
40
+ def __init__(self, config_id: str = "default", core_num: Optional[int] = None):
41
+ """
42
+ ``Executable`` for bash script "palmrun".
43
+
44
+ :param config_id: Configuration identifier of ``PALM``, defaults to "default"
45
+ :type config_id: str, optional
46
+ :param core_num: CPU cores to use, defaults to None
47
+ :type core_num: Optional[int], optional
48
+ """
49
+ if isinstance(core_num, int) and core_num <= 0:
50
+ logger.warning("`core_num` should be greater than 0")
51
+ core_num = None
52
+
53
+ mpi_use = False
54
+ mpi_cmd = None
55
+ mpi_core_num = None
56
+
57
+ cmd = f"./palmrun -r wrfrun -c {config_id} -a d3# -X {core_num} -v"
58
+
59
+ super().__init__("palmrun", cmd, get_palm_workspace_path(), mpi_use, mpi_cmd, mpi_core_num)
60
+
61
+ _check_and_prepare_namelist()
62
+
63
+ def generate_custom_config(self):
64
+ """
65
+ Store custom configs, including:
66
+
67
+ * Namelist settings.
68
+ """
69
+ self.custom_config.update({"namelist": WRFRUN.config.get_namelist("palm")})
70
+
71
+ def load_custom_config(self):
72
+ """
73
+ Load custom configs, including:
74
+
75
+ * Namelist settings.
76
+ """
77
+ WRFRUN.config.update_namelist(self.custom_config["namelist"], "palm")
78
+
79
+ def before_exec(self):
80
+ WRFRUN.config.check_wrfrun_context(True)
81
+ WRFRUN.config.WRFRUN_WORK_STATUS = "palm"
82
+
83
+ config = WRFRUN.config.get_model_config("palm")
84
+
85
+ if not WRFRUN.config.IS_IN_REPLAY:
86
+ # check if user provides topography files
87
+ if exists(config["topography_file"]):
88
+ self.add_input_files(
89
+ {
90
+ "file_path": config["topography_file"],
91
+ "save_path": get_palm_workspace_path("input"),
92
+ "save_name": "palmrun.topo",
93
+ "is_data": True,
94
+ "is_output": False,
95
+ }
96
+ )
97
+
98
+ super().before_exec()
99
+
100
+ WRFRUN.config.write_namelist(f"{get_palm_workspace_path('input')}/{NamelistName.PALM}", "palm")
101
+
102
+ # print debug logs
103
+ logger.debug("Namelist settings of 'palmrun':")
104
+ logger.debug(WRFRUN.config.get_namelist("palm"))
105
+
106
+ def after_exec(self):
107
+ if not WRFRUN.config.IS_IN_REPLAY:
108
+ self.add_output_files(
109
+ output_dir=get_palm_workspace_path("output"), save_path=self._output_save_path, startswith="wrfrun_"
110
+ )
111
+
112
+ super().after_exec()
113
+
114
+ logger.info(f"All PALM output files have been copied to {WRFRUN.config.parse_resource_uri(self._output_save_path)}")
115
+
116
+
117
+ def palmrun():
118
+ """
119
+ Function interface for :class:`PALMRun`.
120
+
121
+ Parameters needed to initialize :class:`PALMRun` is read from global variable :doc:`WRFRUN </api/core.core>`.
122
+ """
123
+ config = WRFRUN.config.get_model_config("palm")
124
+ PALMRun(config["config_identifier"], WRFRUN.config.get_core_num())()
125
+
126
+
127
+ def _exec_register_func(exec_db: ExecutableDB):
128
+ """
129
+ Function to register ``Executable``.
130
+
131
+ :param exec_db: ``ExecutableDB`` instance.
132
+ :type exec_db: ExecutableDB
133
+ """
134
+ class_list = [PALMRun]
135
+ class_id_list = ["palmrun"]
136
+
137
+ for _class, _id in zip(class_list, class_id_list):
138
+ if not exec_db.is_registered(_id):
139
+ exec_db.register_exec(_id, _class)
140
+
141
+
142
+ WRFRUN.set_exec_db_register_func(_exec_register_func)
143
+
144
+
145
+ __all__ = ["PALMRun", "palmrun"]
@@ -0,0 +1,33 @@
1
+ """
2
+ wrfrun.model.palm.namelist
3
+ ##########################
4
+
5
+ Functions to process namelist for ``PALM``.
6
+
7
+ .. autosummary::
8
+ :toctree: generated/
9
+
10
+ prepare_palm_namelist
11
+ """
12
+
13
+ from os.path import exists
14
+
15
+ from wrfrun.core import WRFRUN
16
+ from wrfrun.log import logger
17
+
18
+
19
+ def prepare_palm_namelist():
20
+ """
21
+ This function loads user PALM namelist file and save it in :doc:`WRFRUN </api/core.core>`.
22
+ """
23
+ palm_config = WRFRUN.config.get_model_config("palm")
24
+ namelist_file = palm_config["user_namelist"]
25
+
26
+ if not exists(namelist_file):
27
+ logger.error(f"Can't find PALM namelist: {namelist_file}")
28
+ raise FileNotFoundError(f"Can't find PALM namelist: {namelist_file}")
29
+
30
+ WRFRUN.config.read_namelist(namelist_file, "palm")
31
+
32
+
33
+ __all__ = ["prepare_palm_namelist"]
wrfrun/model/plot.py CHANGED
@@ -1,5 +1,21 @@
1
+ """
2
+ wrfrun.model.plot
3
+ #################
4
+
5
+ This module is used to plot domain area so users can check their domain settings before running simulation.
6
+
7
+ .. autosummary::
8
+ :toctree: generated/
9
+
10
+ _calculate_x_y_offset
11
+ create_projection
12
+ parse_domain_setting
13
+ plot_domain_area
14
+ generate_domain_area
15
+ """
16
+
1
17
  from os.path import abspath, exists
2
- from typing import Literal, Optional, TypedDict, Union
18
+ from typing import Union
3
19
 
4
20
  import cartopy.feature as cfeature
5
21
  import f90nml
@@ -8,31 +24,14 @@ from cartopy import crs
8
24
  from cartopy.mpl.geoaxes import GeoAxes
9
25
  from cartopy.mpl.gridliner import LATITUDE_FORMATTER, LONGITUDE_FORMATTER
10
26
  from haversine.haversine import Direction, Unit, inverse_haversine
27
+ from matplotlib.figure import Figure
11
28
 
12
- from wrfrun.core import get_wrfrun_config
13
- from wrfrun.utils import check_path, logger
29
+ from ..core import WRFRUN
30
+ from ..log import logger
31
+ from ..utils import check_path
32
+ from .type import DomainSetting
14
33
 
15
34
 
16
- class DomainSetting(TypedDict):
17
- """
18
- Domain settings which can be used to create a projection.
19
- """
20
- dx: int
21
- dy: int
22
- e_sn: Union[list[int], tuple[int]]
23
- e_we: Union[list[int], tuple[int]]
24
- i_parent_start: Union[list[int], tuple[int]]
25
- j_parent_start: Union[list[int], tuple[int]]
26
- max_dom: int
27
- parent_grid_ratio: Union[list[int], tuple[int]]
28
- map_proj: Literal["lambert", "polar", "mercator", "lat-lon"]
29
- ref_lat: Union[int, float]
30
- ref_lon: Union[int, float]
31
- truelat1: Union[int, float]
32
- truelat2: Union[int, float]
33
- stand_lon: Union[int, float]
34
-
35
-
36
35
  def _calculate_x_y_offset(domain_settings: DomainSetting) -> tuple[float, float]:
37
36
  """
38
37
  Calculate X and Y offset from planar origin in metres.
@@ -42,12 +41,14 @@ def _calculate_x_y_offset(domain_settings: DomainSetting) -> tuple[float, float]
42
41
  :return: (X offset, Y offset)
43
42
  :rtype: tuple
44
43
  """
45
- false_easting = (domain_settings["e_we"][0] - 1) / 2 * domain_settings["dx"]
46
- false_northing = (domain_settings["e_sn"][0] - 1) / 2 * domain_settings["dy"]
44
+ false_easting = (domain_settings["points_x"][0] - 1) / 2 * domain_settings["resolution_x"]
45
+ false_northing = (domain_settings["points_y"][0] - 1) / 2 * domain_settings["resolution_y"]
47
46
  return false_easting, false_northing
48
47
 
49
48
 
50
- def create_projection(domain_settings: DomainSetting) -> crs.Projection:
49
+ def create_projection(
50
+ domain_settings: DomainSetting,
51
+ ) -> Union[crs.LambertConformal, crs.NorthPolarStereo, crs.SouthPolarStereo, crs.Mercator, crs.PlateCarree]:
51
52
  """
52
53
  Create a projection from domain settings which can be used to draw images.
53
54
 
@@ -59,26 +60,25 @@ def create_projection(domain_settings: DomainSetting) -> crs.Projection:
59
60
  :return: Projection object and the used domain settings.
60
61
  :rtype: (Projection, domain settings)
61
62
  """
62
- match domain_settings["map_proj"]:
63
+ # declare the proj to pass static type check
64
+ proj = None
63
65
 
66
+ match domain_settings["projection_type"]:
64
67
  case "lat-lon":
65
- proj = crs.PlateCarree(central_longitude=domain_settings["ref_lon"])
68
+ proj = crs.PlateCarree(central_longitude=domain_settings["reference_lon"])
66
69
 
67
70
  case "lambert":
68
71
  false_easting, false_northing = _calculate_x_y_offset(domain_settings)
69
72
  proj = crs.LambertConformal(
70
- central_longitude=domain_settings["ref_lon"],
71
- central_latitude=domain_settings["ref_lat"],
72
- standard_parallels=(
73
- domain_settings["truelat1"],
74
- domain_settings["truelat2"]
75
- ),
73
+ central_longitude=domain_settings["reference_lon"],
74
+ central_latitude=domain_settings["reference_lat"],
75
+ standard_parallels=(domain_settings["true_lat1"], domain_settings["true_lat2"]),
76
76
  false_easting=false_easting,
77
- false_northing=false_northing
77
+ false_northing=false_northing,
78
78
  )
79
79
 
80
80
  case "polar":
81
- ref_lat = domain_settings["ref_lat"]
81
+ ref_lat = domain_settings["reference_lat"]
82
82
  if ref_lat > 0:
83
83
  proj = crs.NorthPolarStereo(central_longitude=domain_settings["stand_lon"])
84
84
 
@@ -97,17 +97,18 @@ def create_projection(domain_settings: DomainSetting) -> crs.Projection:
97
97
  # ref_lat_distance = ref_lat_distance if central_latitude < 0 else -ref_lat_distance
98
98
  # false_northing = ref_lat_distance + false_northing
99
99
  proj = crs.Mercator(
100
- central_longitude=domain_settings["ref_lon"],
101
- latitude_true_scale=domain_settings["truelat1"],
100
+ central_longitude=domain_settings["reference_lon"],
101
+ latitude_true_scale=domain_settings["true_lat1"],
102
102
  # false_northing=false_northing,
103
103
  # false_easting=false_easting
104
104
  )
105
105
 
106
- case _:
107
- logger.error(f"Unknown projection name: {domain_settings['map_proj']}")
108
- raise KeyError(f"Unknown projection name: {domain_settings['map_proj']}")
106
+ if proj is not None:
107
+ return proj
109
108
 
110
- return proj
109
+ else:
110
+ logger.error(f"Unknown projection name: {domain_settings['projection_type']}")
111
+ raise KeyError(f"Unknown projection name: {domain_settings['projection_type']}")
111
112
 
112
113
 
113
114
  def parse_domain_setting(namelist: Union[str, dict]) -> DomainSetting:
@@ -140,30 +141,26 @@ def parse_domain_setting(namelist: Union[str, dict]) -> DomainSetting:
140
141
  namelist = f90nml.read(namelist).todict()
141
142
 
142
143
  domain_setting: DomainSetting = {
143
- "dx": namelist["geogrid"]["dx"],
144
- "dy": namelist["geogrid"]["dy"],
145
- "e_sn": namelist["geogrid"]["e_sn"],
146
- "e_we": namelist["geogrid"]["e_we"],
147
- "i_parent_start": namelist["geogrid"]["i_parent_start"],
148
- "j_parent_start": namelist["geogrid"]["j_parent_start"],
149
- "max_dom": namelist["share"]["max_dom"],
150
- "parent_grid_ratio": namelist["geogrid"]["parent_grid_ratio"],
151
- "ref_lat": namelist["geogrid"]["ref_lat"],
152
- "ref_lon": namelist["geogrid"]["ref_lon"],
153
- "truelat1": namelist["geogrid"]["truelat1"],
154
- "truelat2": namelist["geogrid"]["truelat2"],
144
+ "resolution_x": namelist["geogrid"]["dx"],
145
+ "resolution_y": namelist["geogrid"]["dy"],
146
+ "points_y": namelist["geogrid"]["e_sn"],
147
+ "points_x": namelist["geogrid"]["e_we"],
148
+ "x_parent_index": namelist["geogrid"]["i_parent_start"],
149
+ "y_parent_index": namelist["geogrid"]["j_parent_start"],
150
+ "domain_num": namelist["share"]["max_dom"],
151
+ "grid_spacing_ratio": namelist["geogrid"]["parent_grid_ratio"],
152
+ "reference_lat": namelist["geogrid"]["ref_lat"],
153
+ "reference_lon": namelist["geogrid"]["ref_lon"],
154
+ "true_lat1": namelist["geogrid"]["truelat1"],
155
+ "true_lat2": namelist["geogrid"]["truelat2"],
155
156
  "stand_lon": namelist["geogrid"]["stand_lon"],
156
- "map_proj": namelist["geogrid"]["map_proj"]
157
+ "projection_type": namelist["geogrid"]["map_proj"],
157
158
  }
158
159
 
159
160
  return domain_setting
160
161
 
161
162
 
162
- def plot_domain_area(
163
- fig: plt.Figure,
164
- domain_settings: Optional[DomainSetting] = None,
165
- model_name: Optional[str] = None
166
- ):
163
+ def plot_domain_area(fig: Figure, domain_settings: DomainSetting):
167
164
  """
168
165
  Plot domain area based on domain settings.
169
166
 
@@ -175,78 +172,53 @@ def plot_domain_area(
175
172
  :type fig: Figure
176
173
  :param domain_settings: Dictionary contains domain settings. If None, read domain settings from ``WRFRUNConfig``.
177
174
  :type domain_settings: DomainSetting | None
178
- :param model_name: Model's name for reading domain settings.
179
- :type model_name: str | None
180
175
  """
181
- if domain_settings is None:
182
- if model_name is None:
183
- logger.error("You need to give 'model_name' if `domain_settings == None`")
184
- raise ValueError("You need to give 'model_name' if `domain_settings == None`")
185
-
186
- user_settings = get_wrfrun_config().get_model_config(model_name)["domain"]
187
- domain_settings: DomainSetting = {
188
- "dx": user_settings["dx"],
189
- "dy": user_settings["dy"],
190
- "e_sn": user_settings["e_sn"],
191
- "e_we": user_settings["e_we"],
192
- "i_parent_start": user_settings["i_parent_start"],
193
- "j_parent_start": user_settings["j_parent_start"],
194
- "max_dom": user_settings["domain_num"],
195
- "parent_grid_ratio": user_settings["parent_grid_ratio"],
196
- "ref_lat": user_settings["ref_lat"],
197
- "ref_lon": user_settings["ref_lon"],
198
- "truelat1": user_settings["truelat1"],
199
- "truelat2": user_settings["truelat2"],
200
- "stand_lon": user_settings["stand_lon"],
201
- "map_proj": user_settings["map_proj"]
202
- }
203
-
204
176
  proj = create_projection(domain_settings)
205
177
 
206
178
  fig.clear()
207
- ax: GeoAxes = fig.add_subplot(1, 1, 1, projection=proj) # type: ignore
179
+ ax: GeoAxes = fig.add_subplot(1, 1, 1, projection=proj) # type: ignore
208
180
  ax.coastlines(resolution="50m")
209
181
  ax.add_feature(cfeature.OCEAN)
210
182
  ax.add_feature(cfeature.LAND)
211
183
 
212
184
  # set gridline attributes, close the default labels
213
185
  grid_line = ax.gridlines(
214
- draw_labels=True, dms=True, linestyle=":", linewidth=0.3,
215
- x_inline=False, y_inline=False, color='k'
186
+ draw_labels=True,
187
+ dms=True,
188
+ linestyle=":",
189
+ linewidth=0.3,
190
+ x_inline=False,
191
+ y_inline=False,
192
+ color="k",
193
+ rotate_labels=None,
194
+ xformatter=LONGITUDE_FORMATTER,
195
+ yformatter=LATITUDE_FORMATTER,
216
196
  )
217
197
 
218
198
  # close coordinates labels on the top and right
219
199
  grid_line.top_labels = False
220
200
  grid_line.right_labels = False
221
201
 
222
- # align coordinates labels
223
- grid_line.rotate_labels = None
224
-
225
- # set label formatter
226
- grid_line.xformattter = LONGITUDE_FORMATTER
227
- grid_line.yformatter = LATITUDE_FORMATTER
228
202
  ax.set_title("Domain Configuration")
229
203
 
230
204
  # set area range
231
- match type(proj):
232
-
233
- case crs.Mercator:
234
- # we may need to calculate the range of longitude and latitude
235
- ref_lon = domain_settings["ref_lon"]
236
- ref_lat = domain_settings["ref_lat"]
237
- false_easting, false_northing = _calculate_x_y_offset(domain_settings)
238
- _, start_lon = inverse_haversine((ref_lat, ref_lon), false_easting, direction=Direction.WEST, unit=Unit.METERS)
239
- _, end_lon = inverse_haversine((ref_lat, ref_lon), false_easting, direction=Direction.EAST, unit=Unit.METERS)
240
- start_lat, _ = inverse_haversine((ref_lat, ref_lon), false_northing, direction=Direction.SOUTH, unit=Unit.METERS)
241
- end_lat, _ = inverse_haversine((ref_lat, ref_lon), false_northing, direction=Direction.NORTH, unit=Unit.METERS)
242
- ax.set_extent([start_lon, end_lon, start_lat, end_lat])
205
+ if isinstance(proj, crs.Mercator):
206
+ # we may need to calculate the range of longitude and latitude
207
+ ref_lon = domain_settings["reference_lon"]
208
+ ref_lat = domain_settings["reference_lat"]
209
+ false_easting, false_northing = _calculate_x_y_offset(domain_settings)
210
+ _, start_lon = inverse_haversine((ref_lat, ref_lon), false_easting, direction=Direction.WEST, unit=Unit.METERS)
211
+ _, end_lon = inverse_haversine((ref_lat, ref_lon), false_easting, direction=Direction.EAST, unit=Unit.METERS)
212
+ start_lat, _ = inverse_haversine((ref_lat, ref_lon), false_northing, direction=Direction.SOUTH, unit=Unit.METERS)
213
+ end_lat, _ = inverse_haversine((ref_lat, ref_lon), false_northing, direction=Direction.NORTH, unit=Unit.METERS)
214
+ ax.set_extent([start_lon, end_lon, start_lat, end_lat])
243
215
 
244
- case crs.LambertConformal:
245
- false_easting, false_northing = _calculate_x_y_offset(domain_settings)
246
- ax.set_extent([0, false_easting * 2, 0, false_northing * 2], crs=proj)
216
+ elif isinstance(proj, crs.LambertConformal):
217
+ false_easting, false_northing = _calculate_x_y_offset(domain_settings)
218
+ ax.set_extent([0, false_easting * 2, 0, false_northing * 2], crs=proj)
247
219
 
248
- case _:
249
- logger.error(f"Unsupported project type: {type(proj)}")
220
+ else:
221
+ logger.error(f"Unsupported project type: {type(proj)}")
250
222
 
251
223
 
252
224
  def generate_domain_area():
@@ -254,24 +226,32 @@ def generate_domain_area():
254
226
  Generate domain area for each model based on user's config.
255
227
  Images are saved to the output directory with name: "${model_name}_domain.png".
256
228
 
257
- :return:
258
- :rtype:
229
+ :return: True if domain area is ploted, else False.
230
+ :rtype: bool
259
231
  """
260
- WRFRUNConfig = get_wrfrun_config()
232
+ WRFRUNConfig = WRFRUN.config
261
233
  save_path = WRFRUNConfig.parse_resource_uri(WRFRUNConfig.WRFRUN_OUTPUT_PATH)
262
234
  check_path(save_path)
263
235
  save_path = abspath(save_path)
264
236
 
265
237
  fig = plt.figure(figsize=(10.24, 10.24))
266
238
 
239
+ flag = False
240
+
267
241
  model_configs = WRFRUNConfig["model"]
268
242
  for model_name in model_configs:
269
- plot_domain_area(fig, model_name=model_name)
243
+ if model_name in ["wrf"] and model_configs[model_name]["use"]:
244
+ namelist = model_configs[model_name]
245
+ plot_domain_area(fig, parse_domain_setting(namelist))
246
+
247
+ _save_path = f"{save_path}/{model_name}_domain.png"
248
+ fig.savefig(_save_path)
249
+
250
+ logger.info(f"Save domain image for '{model_name}' to '{_save_path}'")
270
251
 
271
- _save_path = f"{save_path}/{model_name}_domain.png"
272
- fig.savefig(_save_path)
252
+ flag = True
273
253
 
274
- logger.info(f"Save domain image for '{model_name}' to '{_save_path}'")
254
+ return flag
275
255
 
276
256
 
277
- __all__ = ["plot_domain_area", "DomainSetting", "create_projection", "parse_domain_setting", "generate_domain_area"]
257
+ __all__ = ["plot_domain_area", "create_projection", "generate_domain_area"]