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
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
|
|
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
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
13
|
-
from
|
|
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["
|
|
46
|
-
false_northing = (domain_settings["
|
|
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(
|
|
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
|
-
|
|
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["
|
|
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["
|
|
71
|
-
central_latitude=domain_settings["
|
|
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["
|
|
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["
|
|
101
|
-
latitude_true_scale=domain_settings["
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
raise KeyError(f"Unknown projection name: {domain_settings['map_proj']}")
|
|
106
|
+
if proj is not None:
|
|
107
|
+
return proj
|
|
109
108
|
|
|
110
|
-
|
|
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
|
-
"
|
|
144
|
-
"
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"
|
|
152
|
-
"
|
|
153
|
-
"
|
|
154
|
-
"
|
|
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
|
-
"
|
|
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)
|
|
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,
|
|
215
|
-
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
249
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
272
|
-
fig.savefig(_save_path)
|
|
252
|
+
flag = True
|
|
273
253
|
|
|
274
|
-
|
|
254
|
+
return flag
|
|
275
255
|
|
|
276
256
|
|
|
277
|
-
__all__ = ["plot_domain_area", "
|
|
257
|
+
__all__ = ["plot_domain_area", "create_projection", "generate_domain_area"]
|