icon4py-standalone-driver 0.2.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.
- icon4py/model/standalone_driver/__init__.py +29 -0
- icon4py/model/standalone_driver/config.py +43 -0
- icon4py/model/standalone_driver/driver_constants.py +28 -0
- icon4py/model/standalone_driver/driver_states.py +183 -0
- icon4py/model/standalone_driver/driver_utils.py +602 -0
- icon4py/model/standalone_driver/main.py +105 -0
- icon4py/model/standalone_driver/py.typed +0 -0
- icon4py/model/standalone_driver/standalone_driver.py +703 -0
- icon4py/model/standalone_driver/testcases/__init__.py +7 -0
- icon4py/model/standalone_driver/testcases/initial_condition.py +372 -0
- icon4py/model/standalone_driver/testcases/utils.py +221 -0
- icon4py_standalone_driver-0.2.0.dist-info/METADATA +64 -0
- icon4py_standalone_driver-0.2.0.dist-info/RECORD +16 -0
- icon4py_standalone_driver-0.2.0.dist-info/WHEEL +5 -0
- icon4py_standalone_driver-0.2.0.dist-info/entry_points.txt +2 -0
- icon4py_standalone_driver-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# ICON4Py - ICON inspired code in Python and GT4Py
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2022-2024, ETH Zurich and MeteoSwiss
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Please, refer to the LICENSE file in the root directory.
|
|
7
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
8
|
+
|
|
9
|
+
from typing import Final
|
|
10
|
+
|
|
11
|
+
from packaging import version as pkg_version
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"__author__",
|
|
16
|
+
"__copyright__",
|
|
17
|
+
"__license__",
|
|
18
|
+
"__version__",
|
|
19
|
+
"__version_info__",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
__author__: Final = "ETH Zurich, MeteoSwiss and individual contributors"
|
|
24
|
+
__copyright__: Final = "Copyright (c) 2022-2024 ETH Zurich and MeteoSwiss"
|
|
25
|
+
__license__: Final = "BSD-3-Clause"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
__version__: Final = "0.2.0"
|
|
29
|
+
__version_info__: Final = pkg_version.parse(__version__)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# ICON4Py - ICON inspired code in Python and GT4Py
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2022-2024, ETH Zurich and MeteoSwiss
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Please, refer to the LICENSE file in the root directory.
|
|
7
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
8
|
+
|
|
9
|
+
import dataclasses
|
|
10
|
+
import datetime
|
|
11
|
+
import pathlib
|
|
12
|
+
|
|
13
|
+
from gt4py.next.instrumentation import metrics as gtx_metrics
|
|
14
|
+
|
|
15
|
+
from icon4py.model.common import type_alias as ta
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclasses.dataclass
|
|
19
|
+
class ProfilingStats:
|
|
20
|
+
gt4py_metrics_level: int = gtx_metrics.ALL
|
|
21
|
+
gt4py_metrics_output_file: str = "gt4py_metrics.json"
|
|
22
|
+
skip_first_timestep: bool = True
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclasses.dataclass(frozen=True)
|
|
26
|
+
class DriverConfig:
|
|
27
|
+
"""
|
|
28
|
+
Standalone driver configuration.
|
|
29
|
+
|
|
30
|
+
Default values should correspond to default values in ICON.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
experiment_name: str
|
|
34
|
+
output_path: pathlib.Path
|
|
35
|
+
profiling_stats: ProfilingStats | None
|
|
36
|
+
dtime: datetime.timedelta = datetime.timedelta(seconds=600.0)
|
|
37
|
+
start_date: datetime.datetime = datetime.datetime(1, 1, 1, 0, 0, 0)
|
|
38
|
+
end_date: datetime.datetime = datetime.datetime(1, 1, 1, 1, 0, 0)
|
|
39
|
+
apply_extra_second_order_divdamp: bool = False
|
|
40
|
+
vertical_cfl_threshold: ta.wpfloat = dataclasses.field(default_factory=lambda: ta.wpfloat(0.85))
|
|
41
|
+
ndyn_substeps: int = 5
|
|
42
|
+
enable_statistics_output: bool = False
|
|
43
|
+
ntracer: int = 0
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# ICON4Py - ICON inspired code in Python and GT4Py
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2022-2024, ETH Zurich and MeteoSwiss
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Please, refer to the LICENSE file in the root directory.
|
|
7
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
8
|
+
|
|
9
|
+
from icon4py.model.common import type_alias as ta
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
#: Factor multiplied to the user-defined CFL number to determine the whether to enter watchmode
|
|
13
|
+
CFL_ENTER_WATCHMODE_FACTOR = ta.wpfloat("0.81")
|
|
14
|
+
|
|
15
|
+
#: Threshold factor multiplied to the user-defined CFL number to maintain the number of substeps
|
|
16
|
+
CFL_THRESHOLD_FACTOR = ta.wpfloat("0.9")
|
|
17
|
+
|
|
18
|
+
#: Factor multiplied to the user-defined CFL number to determine the whether to leave watchmode
|
|
19
|
+
CFL_LEAVE_WATCHMODE_FACTOR = ta.wpfloat("0.76")
|
|
20
|
+
|
|
21
|
+
#: Adjustment factor for second order divergence damping
|
|
22
|
+
ADJUST_FACTOR_FOR_SECOND_ORDER_DIVDAMP = ta.wpfloat("0.8")
|
|
23
|
+
|
|
24
|
+
#: Initial period for second order divergence damping
|
|
25
|
+
INITIAL_PERIOD_FOR_SECOND_ORDER_DIVDAMP = ta.wpfloat("1800.0")
|
|
26
|
+
|
|
27
|
+
#: Transition end period for second order divergence damping
|
|
28
|
+
TRANSITION_END_PERIOD_FOR_SECOND_ORDER_DIVDAMP = ta.wpfloat("7200.0")
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# ICON4Py - ICON inspired code in Python and GT4Py
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2022-2024, ETH Zurich and MeteoSwiss
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Please, refer to the LICENSE file in the root directory.
|
|
7
|
+
# SPDX-License-Identifier: BSD-3-Clause
|
|
8
|
+
|
|
9
|
+
import dataclasses
|
|
10
|
+
import datetime
|
|
11
|
+
import enum
|
|
12
|
+
import functools
|
|
13
|
+
import logging
|
|
14
|
+
import statistics
|
|
15
|
+
from typing import NamedTuple
|
|
16
|
+
|
|
17
|
+
import devtools
|
|
18
|
+
|
|
19
|
+
import icon4py.model.common.utils as common_utils
|
|
20
|
+
from icon4py.model.atmosphere.advection import advection_states
|
|
21
|
+
from icon4py.model.atmosphere.diffusion import diffusion_states
|
|
22
|
+
from icon4py.model.atmosphere.dycore import dycore_states
|
|
23
|
+
from icon4py.model.common import type_alias as ta
|
|
24
|
+
from icon4py.model.common.grid import geometry as grid_geometry
|
|
25
|
+
from icon4py.model.common.interpolation import interpolation_factory
|
|
26
|
+
from icon4py.model.common.metrics import metrics_factory
|
|
27
|
+
from icon4py.model.common.states import (
|
|
28
|
+
diagnostic_state as diagnostics,
|
|
29
|
+
prognostic_state as prognostics,
|
|
30
|
+
)
|
|
31
|
+
from icon4py.model.standalone_driver import config as driver_config
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
log = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class StaticFieldFactories(NamedTuple):
|
|
38
|
+
"""
|
|
39
|
+
Factories of static fields for the driver and components.
|
|
40
|
+
|
|
41
|
+
Attributes:
|
|
42
|
+
geometry_field_source: grid geometry field factory that stores geometrical properties of a grid
|
|
43
|
+
interpolation_field_source: interpolation field factory that stores pre-computed coefficients for interpolation employed in the model
|
|
44
|
+
metrics_field_source: metrics field factory that stores pre-computed coefficients for numerical operations employed in the model
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
geometry_field_source: grid_geometry.GridGeometry
|
|
48
|
+
interpolation_field_source: interpolation_factory.InterpolationFieldsFactory
|
|
49
|
+
metrics_field_source: metrics_factory.MetricsFieldsFactory
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class DriverStates(NamedTuple):
|
|
53
|
+
"""
|
|
54
|
+
Initialized states for the driver run.
|
|
55
|
+
|
|
56
|
+
Attributes:
|
|
57
|
+
prep_advection_prognostic: Fields collecting data for advection during the solve nonhydro timestep.
|
|
58
|
+
solve_nonhydro_diagnostic: Initial state for solve_nonhydro diagnostic variables.
|
|
59
|
+
diffusion_diagnostic: Initial state for diffusion diagnostic variables.
|
|
60
|
+
prognostics: Initial state for prognostic variables (double buffered).
|
|
61
|
+
diagnostic: Initial state for global diagnostic variables.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
prep_advection_prognostic: dycore_states.PrepAdvection
|
|
65
|
+
solve_nonhydro_diagnostic: dycore_states.DiagnosticStateNonHydro
|
|
66
|
+
tracer_advection_diagnostic: advection_states.AdvectionDiagnosticState
|
|
67
|
+
prep_tracer_advection_prognostic: advection_states.AdvectionPrepAdvState
|
|
68
|
+
diffusion_diagnostic: diffusion_states.DiffusionDiagnosticState
|
|
69
|
+
prognostics: common_utils.TimeStepPair[prognostics.PrognosticState]
|
|
70
|
+
diagnostic: diagnostics.DiagnosticState
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclasses.dataclass
|
|
74
|
+
class ModelTimeVariables:
|
|
75
|
+
"""
|
|
76
|
+
This class contains driver's run-time time or date variables.
|
|
77
|
+
It tracks the current simulation date, substepping information, and cfl watch mode.
|
|
78
|
+
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
config: dataclasses.InitVar[driver_config.DriverConfig]
|
|
82
|
+
|
|
83
|
+
n_time_steps: int = dataclasses.field(init=False)
|
|
84
|
+
dtime: datetime.timedelta = dataclasses.field(init=False)
|
|
85
|
+
ndyn_substeps_var: int = dataclasses.field(init=False)
|
|
86
|
+
max_ndyn_substeps: int = dataclasses.field(init=False)
|
|
87
|
+
elapsed_time_in_seconds: ta.wpfloat = dataclasses.field(init=False)
|
|
88
|
+
simulation_date: datetime.datetime = dataclasses.field(init=False)
|
|
89
|
+
is_first_step_in_simulation: bool = dataclasses.field(init=False)
|
|
90
|
+
cfl_watch_mode: bool = dataclasses.field(init=False)
|
|
91
|
+
|
|
92
|
+
def __post_init__(self, config: driver_config.DriverConfig) -> None:
|
|
93
|
+
self.n_time_steps = int((config.end_date - config.start_date) / config.dtime)
|
|
94
|
+
self.dtime = config.dtime
|
|
95
|
+
self.elapsed_time_in_seconds = ta.wpfloat("0.0")
|
|
96
|
+
self.simulation_date = config.start_date
|
|
97
|
+
self.ndyn_substeps_var = config.ndyn_substeps
|
|
98
|
+
self.max_ndyn_substeps = config.ndyn_substeps + 7
|
|
99
|
+
self.is_first_step_in_simulation = True
|
|
100
|
+
self.cfl_watch_mode = False
|
|
101
|
+
|
|
102
|
+
if self.n_time_steps < 0:
|
|
103
|
+
raise ValueError("end_date should be larger than start_date. Please check.")
|
|
104
|
+
|
|
105
|
+
@functools.cached_property
|
|
106
|
+
def dtime_in_seconds(self) -> ta.wpfloat:
|
|
107
|
+
return ta.wpfloat(self.dtime.total_seconds())
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def substep_timestep(self) -> ta.wpfloat:
|
|
111
|
+
return ta.wpfloat(self.dtime_in_seconds / self.ndyn_substeps_var)
|
|
112
|
+
|
|
113
|
+
def next_simulation_date(self) -> None:
|
|
114
|
+
self.simulation_date += self.dtime
|
|
115
|
+
self.elapsed_time_in_seconds += self.dtime_in_seconds
|
|
116
|
+
|
|
117
|
+
def update_ndyn_substeps(self, new_ndyn_substeps: int) -> None:
|
|
118
|
+
self.ndyn_substeps_var = new_ndyn_substeps
|
|
119
|
+
|
|
120
|
+
def update_cfl_watch_mode(self, mode: bool) -> None:
|
|
121
|
+
self.cfl_watch_mode = mode
|
|
122
|
+
|
|
123
|
+
def reset(self, config: driver_config.DriverConfig) -> None:
|
|
124
|
+
"""
|
|
125
|
+
Re-initialize all time-integration-related runtime values from the given config.
|
|
126
|
+
"""
|
|
127
|
+
self.__post_init__(config)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class DriverTimers(enum.Enum):
|
|
131
|
+
SOLVE_NH_FIRST_STEP = "solve_nh_first_step"
|
|
132
|
+
SOLVE_NH = "solve_nh"
|
|
133
|
+
DIFFUSION_FIRST_STEP = "diffusion_first_step"
|
|
134
|
+
DIFFUSION = "diffusion"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@dataclasses.dataclass
|
|
138
|
+
class TimerCollection:
|
|
139
|
+
timer_names: dataclasses.InitVar[list[str]]
|
|
140
|
+
timers: dict[str, devtools.Timer] = dataclasses.field(init=False)
|
|
141
|
+
|
|
142
|
+
def __post_init__(self, timer_names: str | list[str]) -> None:
|
|
143
|
+
self.timers = {}
|
|
144
|
+
self.add_timers(timer_names)
|
|
145
|
+
|
|
146
|
+
def add_timers(self, timer_names: str | list[str]) -> None:
|
|
147
|
+
for timer in timer_names:
|
|
148
|
+
assert timer not in self.timers, f"Timer '{timer}' is already defined."
|
|
149
|
+
self.timers[timer] = devtools.Timer(timer, dp=6, verbose=False)
|
|
150
|
+
|
|
151
|
+
def show_timer_report(
|
|
152
|
+
self,
|
|
153
|
+
) -> None:
|
|
154
|
+
log.info("===== ICON4Py timer report =====")
|
|
155
|
+
table_titles = (
|
|
156
|
+
f"|{'timer name':^30}|"
|
|
157
|
+
f"{'no. of times called':^23}|"
|
|
158
|
+
f"{'mean time (s)':^23}|"
|
|
159
|
+
f"{'std. deviation (s)':^23}|"
|
|
160
|
+
f"{'min time (s)':^23}|"
|
|
161
|
+
f"{'max time (s)':^23}|"
|
|
162
|
+
)
|
|
163
|
+
log.info(table_titles)
|
|
164
|
+
log.info("-" * len(table_titles))
|
|
165
|
+
for timer_name, timer in self.timers.items():
|
|
166
|
+
times = []
|
|
167
|
+
for r in timer.results:
|
|
168
|
+
if not r.finish:
|
|
169
|
+
r.capture()
|
|
170
|
+
times.append(r.elapsed())
|
|
171
|
+
if len(times) > 0:
|
|
172
|
+
log.info(
|
|
173
|
+
f"|{timer_name:^30}|"
|
|
174
|
+
f"{len(times):^23}|"
|
|
175
|
+
f"{statistics.mean(times):^23.8f}|"
|
|
176
|
+
f"{statistics.stdev(times) if len(times) > 1 else 0:^23.8f}|"
|
|
177
|
+
f"{min(times):^23.8f}|"
|
|
178
|
+
f"{max(times):^23.8f}|"
|
|
179
|
+
)
|
|
180
|
+
else:
|
|
181
|
+
log.info(
|
|
182
|
+
f"|{timer_name:^30}|{'not started':^23}|{'':^23}|{'':^23}|{'':^23}|{'':^23}|"
|
|
183
|
+
)
|