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.
@@ -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
+ )