-
Notifications
You must be signed in to change notification settings - Fork 11
Physics driver with Muphys #1301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 75 commits
717b3e2
e0bfe9e
c38a902
1466c21
a3bb29a
66617a4
60c9c6a
9f193b9
47f0aa7
b6d166a
c325c5f
b035382
730893f
f74672b
2c61eae
e720c40
4722175
fea8cd8
42ddc80
bb30857
044b2c6
596286b
98be0a6
5ef96df
5f714e2
eac8adb
47d8dab
acac3fd
dd3bc13
c7e780f
6aa8056
acf8a20
701686a
7569596
41cc4ae
8aa91e8
f20e891
dd3dd17
302b573
516bc7e
ffdb42b
46c5d68
cb028d0
ddae38d
7520535
f011e9b
1266ac2
568d35e
c001e14
c40475b
09b8924
4b5b5fb
895ed66
79f1ec0
ba77f68
2437069
2482ce0
3f4d9e6
27ac411
731fa38
c571a20
dee4edd
59cae8d
55f036c
89b4b17
59ed7a6
b347a62
25c2854
fee4c76
21b9bfb
8119c66
ad485d1
2890d83
9524a28
42b46fd
85dd678
bb80c24
246f033
f02be4a
dd3e382
8f3352c
336cdda
874ac28
d36d592
6108160
08399a3
a67a489
d763a33
5a95578
241d6a0
8d2eb57
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,208 @@ | ||
| # ICON4Py - ICON inspired code in Python and GT4Py | ||
| # | ||
| # Copyright (c) 2022-2024, ETH Zurich and MeteoSwiss | ||
| # All rights reserved. | ||
| # | ||
| # Please, refer to the LICENSE file in the root directory. | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import datetime | ||
| import types | ||
| from collections.abc import Callable | ||
| from typing import TYPE_CHECKING, Any, cast | ||
|
|
||
| import gt4py.next as gtx | ||
|
|
||
| from icon4py.model.atmosphere.subgrid_scale_physics.muphys import ( | ||
| config as muphys_config, | ||
| data as muphys_data, | ||
| ) | ||
| from icon4py.model.atmosphere.subgrid_scale_physics.muphys.core.definitions import SPECIES, Q | ||
| from icon4py.model.atmosphere.subgrid_scale_physics.muphys.driver.run_full_muphys import ( | ||
| setup_muphys, | ||
| ) | ||
| from icon4py.model.common import ( | ||
| dimension as dims, | ||
| field_type_aliases as fa, | ||
| model_backends, | ||
| model_options, | ||
| type_alias as ta, | ||
| ) | ||
| from icon4py.model.common.diagnostic_calculations.stencils import calculate_tendency | ||
| from icon4py.model.common.math.stencils import generic_math_operations | ||
|
|
||
|
|
||
| if TYPE_CHECKING: | ||
| import gt4py.next.typing as gtx_typing | ||
|
|
||
| from icon4py.model.common.states import model | ||
|
|
||
|
|
||
| class MuphysComponent: | ||
| """The muphys Granule: a per-process adapter wrapping the muphys microphysics program.""" | ||
|
|
||
| # TODO (Yilu): inherit the Component protocol once it is formalized (deferred to a separate PR). | ||
|
|
||
| inputs_properties = muphys_data.INPUTS_PROPERTIES | ||
| outputs_properties = muphys_data.OUTPUTS_PROPERTIES | ||
|
|
||
| def __init__( | ||
| self, | ||
| ncells: int, | ||
| nlev: int, | ||
|
yiluchen1066 marked this conversation as resolved.
Outdated
|
||
| dtime: datetime.timedelta, | ||
| qnc: float, | ||
| backend: gtx_typing.Backend | None = None, | ||
| *, | ||
| scheme: muphys_config.MuphysScheme = muphys_config.MuphysScheme.KOKKOS_MUPHYS, | ||
| step: Callable[..., Any] | None = None, | ||
|
yiluchen1066 marked this conversation as resolved.
|
||
| ) -> None: | ||
| self._ncells = ncells | ||
| self._nlev = nlev | ||
| self._dt_seconds = dtime.total_seconds() | ||
| self._qnc = qnc | ||
| self._backend = model_options.customize_backend(program=None, backend=backend) | ||
|
|
||
| horizontal_sizes = { | ||
| "horizontal_start": gtx.int32(0), | ||
| "horizontal_end": gtx.int32(self._ncells), | ||
|
yiluchen1066 marked this conversation as resolved.
|
||
| } | ||
| vertical_sizes = {"vertical_start": gtx.int32(0), "vertical_end": gtx.int32(self._nlev)} | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. usually in ICON or other models, there is k level above which microphysics is turned off (usually for manual performance tuning, may be for stability issues in some rare cases). In ICON, it is kstart_moist. How about passing in vertical params? Could you check whether we can remove this manual performance tuning so that we always run the microphysics for all levels?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I will check this
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||
| self._calculate_tendency = model_options.setup_program( | ||
| program=calculate_tendency.calculate_cell_kdim_field_tendency, | ||
| backend=self._backend, | ||
| horizontal_sizes=horizontal_sizes, | ||
| vertical_sizes=vertical_sizes, | ||
| offset_provider={}, | ||
| ) | ||
| self._copy_field = model_options.setup_program( | ||
| program=generic_math_operations.copy_field_on_cell_k, | ||
| backend=self._backend, | ||
| horizontal_sizes=horizontal_sizes, | ||
| vertical_sizes=vertical_sizes, | ||
| offset_provider={}, | ||
| ) | ||
|
|
||
| allocator = model_backends.get_allocator(backend) | ||
|
|
||
| if step is None: | ||
| sizes = types.SimpleNamespace(ncells=ncells, nlev=nlev) | ||
| step = setup_muphys( | ||
| inp=sizes, # type: ignore[arg-type] # only .ncells/.nlev are read | ||
| dt=self._dt_seconds, | ||
| qnc=qnc, | ||
| backend=backend, | ||
| single_program=False, | ||
| scheme=scheme, | ||
| ) | ||
| self._step = step | ||
|
|
||
| cell_k_domain = gtx.domain({dims.CellDim: ncells, dims.KDim: nlev}) | ||
| self._t_out = gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator) | ||
| self._q_out = Q( | ||
| v=gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| c=gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| r=gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| s=gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| i=gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| g=gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| ) | ||
| self._pflx = gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator) | ||
| self._pr = gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator) | ||
| self._ps = gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator) | ||
| self._pi = gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator) | ||
| self._pg = gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator) | ||
| self._pre = gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator) | ||
|
|
||
| self._tendencies: dict[str, fa.CellKField[ta.wpfloat]] = { | ||
| "tend_temperature": gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| "tend_qv": gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| "tend_qc": gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| "tend_qr": gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| "tend_qs": gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| "tend_qi": gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| "tend_qg": gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| } | ||
|
|
||
| self._te_in = gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator) | ||
| self._q_in = Q( | ||
| v=gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| c=gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| r=gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| s=gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| i=gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| g=gtx.zeros(cell_k_domain, dtype=ta.wpfloat, allocator=allocator), | ||
| ) | ||
|
|
||
| def _to_tendency( | ||
| self, | ||
| old: fa.CellKField[ta.wpfloat], | ||
| new: fa.CellKField[ta.wpfloat], | ||
| out: fa.CellKField[ta.wpfloat], | ||
| ) -> None: | ||
| """``out = (new - old) / dt`` over the whole column.""" | ||
| self._calculate_tendency( | ||
| dtime=self._dt_seconds, | ||
| old_field=old, | ||
| new_field=new, | ||
| tendency=out, | ||
| ) | ||
|
yiluchen1066 marked this conversation as resolved.
Outdated
|
||
|
|
||
| def _copy_into(self, src: fa.CellKField[ta.wpfloat], dst: fa.CellKField[ta.wpfloat]) -> None: | ||
| """Copy ``src`` into the granule-owned buffer ``dst``.""" | ||
| self._copy_field( | ||
| field=src, | ||
| output_field=dst, | ||
| ) | ||
|
yiluchen1066 marked this conversation as resolved.
Outdated
|
||
|
|
||
| def __call__( | ||
| self, state: dict[str, model.DataField], time_step: datetime.datetime | ||
| ) -> dict[str, model.DataField]: | ||
|
Comment on lines
+153
to
+155
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question for myself:I suppose this is one of the critical interfaces, though the signature could likely be tightened to something resembling
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Minor note as well that may need broader discussion: If this is indeed the protocol, my preference is to give this a name rather than using the generic
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And further thinking out loud: Could this be based on a more generic
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. and
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. correction: it should be |
||
| """Run muphys, then convert its updated state into tendencies. | ||
|
|
||
| muphys returns updated state (t_out, q_out); this boundary converts it | ||
| to tendencies ``(new - old) / dt`` s. Precip outputs are diagnostics, passed straight through. | ||
| """ | ||
| # cast from generic ``DataField`` to bare gt4py fields | ||
| fields = cast("dict[str, fa.CellKField[ta.wpfloat]]", state) | ||
|
|
||
| self._copy_into(fields["te"], self._te_in) | ||
| for s in SPECIES: | ||
| self._copy_into(fields[f"q{s}"], getattr(self._q_in, s)) | ||
|
|
||
| self._step( | ||
| dz=fields["dz"], | ||
| te=self._te_in, | ||
| p=fields["p"], | ||
| rho=fields["rho"], | ||
| q_in=self._q_in, | ||
| q_out=self._q_out, | ||
| t_out=self._t_out, | ||
| pflx=self._pflx, | ||
| pr=self._pr, | ||
| ps=self._ps, | ||
| pi=self._pi, | ||
| pg=self._pg, | ||
| pre=self._pre, | ||
| ) | ||
|
|
||
| self._to_tendency(fields["te"], self._t_out, self._tendencies["tend_temperature"]) | ||
| for s in SPECIES: | ||
| self._to_tendency( | ||
| fields[f"q{s}"], getattr(self._q_out, s), self._tendencies[f"tend_q{s}"] | ||
| ) | ||
|
|
||
| return cast( | ||
| "dict[str, model.DataField]", | ||
|
yiluchen1066 marked this conversation as resolved.
|
||
| { | ||
| **self._tendencies, | ||
| "pflx": self._pflx, | ||
| "pr": self._pr, | ||
| "ps": self._ps, | ||
| "pi": self._pi, | ||
| "pg": self._pg, | ||
| "pre": self._pre, | ||
| }, | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| # ICON4Py - ICON inspired code in Python and GT4Py | ||
| # | ||
| # Copyright (c) 2022-2024, ETH Zurich and MeteoSwiss | ||
| # All rights reserved. | ||
| # | ||
| # Please, refer to the LICENSE file in the root directory. | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import dataclasses | ||
| import enum | ||
|
|
||
|
|
||
| class MuphysScheme(enum.Enum): | ||
| """Selects between the two graupel microphysics formulations. | ||
|
|
||
| KOKKOS_MUPHYS follows the muphys C++/Kokkos reference implementation (the | ||
| original source of this port, validated by the netCDF-based muphys tests). | ||
| AES_GRAUPEL follows the newer MPIM rain-microphysics revisions carried by | ||
| icon-nwp (mo_aes_graupel.f90), validated against the aes-graupel serialbox | ||
| savepoints. | ||
| """ | ||
|
|
||
| KOKKOS_MUPHYS = "kokkos_muphys" | ||
| AES_GRAUPEL = "aes_graupel" | ||
|
|
||
|
|
||
| @dataclasses.dataclass(frozen=True) | ||
| class MuphysConfig: | ||
| """Configuration for the muphys microphysics component.""" | ||
|
|
||
| qnc: float = 50.0e6 # cloud droplet number concentration [m^-3], matches Fortran cloud_num | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ignore my comment here. This is just for myself.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is replicated in Cell-K array form in fortran, but then aes_graupel always uses the first element of the array. so they had thought about this or even started working on cell-dependent qnc but did not implement it in the end |
||
| scheme: MuphysScheme = MuphysScheme.AES_GRAUPEL # the driver validates against icon-nwp | ||
|
yiluchen1066 marked this conversation as resolved.
Outdated
|
||
Uh oh!
There was an error while loading. Please reload this page.