# Graupel scheme ###### tags: `functional cycle 12` shaped by: ## Goals Full implementation of the Graupel scheme with integration into ICON Fortran. Additionally, GT4Py is extend by features. Workarounds should be used until the features become available. ### Not (yet) goals: - Performance - Code beauty within the boundaries of GT4Py (and possible improvements realized in this project) ## Appetite Proposal: full cycle ## Solution ### Port the scheme Expand https://github.com/leuty/graupel_standalone/tree/graupel_stub to the full scheme, but only the path that is active in Aquaplanet and MCH production (as discussed in the EXCLAIM developer meeting). Implementation hints: regularly run the gtfn codegenerator on the partially implemented scheme to detect early if transformation passes can't handle certain constructs. ### Field view extensions and bug fixes In general, there might still be bugs for edge-cases that are triggered by this scheme. Hot fixes should be implemented with high-priority if no work-arounds exist. It might be convenient to keep a special branch with all required features, if they are implemented, but not merged to the main GT4Py branch. #### For/while loop: Peter (WIP) Description: Support point-wise/per-grid-point loops. Workaround: Use manually unrolled loop. Alternatives are the proposed `scalar_operator` or the `functional for-loop` discussed in the following. ##### Functional while/for loop Implement the [jax while_loop](https://jax.readthedocs.io/en/latest/_autosummary/jax.lax.while_loop.html). The simplified satad loop would look like ```python! def cond(x: tuple[Field[...], Field[...],...]): # `...` is used as placeholder, not Python elipses counter, maxiter, twork, tworkold, tol = x return count < maxiter and twork-tworkold>tol def body(x: ...): counter, maxiter, twork, tworkold, tol = x return (counter+1, maxiter, compute_twork_next(...), twork, tol) def newtonian_loop(...): return while_loop(cond, body, (0, 10, twork_init, twork_init+10., 1e-3)) ``` *Steps* - Implement a `while_loop(cond, body, init)` builtin on iterator level, where `cond` and `body` are functions and `init` is a tuple of values (see [jax while_loop](https://jax.readthedocs.io/en/latest/_autosummary/jax.lax.while_loop.html) for details on semantics). First in the embedded backend and later also for the C++ backend. The C++ backend tasks can be done independently of the frontend tasks. - Implement a `while_loop` builtin in the frontend, where `cond` and `body` are two field operators and `init` is again a tuple of values (may be fields). ```python! @field_operator def cond(counter: int, maxiter: int, twork: Field[...], tworkold: Field[...], tol: float) -> Field[..., bool]: return count < maxiter and (twork - tworkold) > tol @field_operator def body(counter: int, maxiter: int, twork: Field[...], tworkold: Field[...], tol: float) -> tuple[int, int, Field[...], Field[...], float]: twork_next = ... return (counter+1, maxiter, twork_next, twork, tol) @field_operator def newtonian_loop(twork_init: Field[...]) -> Field[...]: return while_loop(cond, body, (0, 10, twork_init, Inf, 1e-3)) ``` Make sure that the example also works for `twork` being a scalar and `newtonian_loop` called from a scan operator (at least for embedded). - Improvements: Extend the Parser (`func_to_foast.py`) and lowering (`foast_to_itir.py`) to support nested functions inside of field operators. The current infrastracture should support this without major changes. Nested functions are lowered using the existing `visit_FunctionDefinition` and each definition will - similarly to assignments - become a let statement with the `init-form` being a lambda function. ```python! @field_operator def newtonian_loop(twork_init: float, maxiter: int, tol: float) -> float: def cond(counter: int, twork: Field[...], tworkold: Field[...]) -> Field[..., bool]: return count < maxiter and (twork - tworkold) > tol def body(counter: int, twork: Field[...], tworkold: Field[...]): twork_next = ... return (counter+1, maxiter, twork_next, twork, tol) return while_loop(cond, body, (0, twork_init, Inf)) newtonian_loop(twork_init, 10, 1e-3) ``` - Extend the quickstart guide with a meaningful, simple example. For example an implementation of a [Newton's method](https://en.wikipedia.org/wiki/Newton%27s_method) computing the zeros of function which is parameterized by what is `twork` above. Tests on all levels required, e.g. tests starting directly from ITIR. #### Cast --> became astype - Nikki (PR merged) Description: introduce a `cast` builtin for casting between datatypes. ##### Syntax **field view**: TODO decide with Enrique `field.astype(dtype)` + `dtype(scalar)` or `cast(field, dtype)` (and the other as syntax sugar later) **iterator IR**: `cast(dtype)(value_or_column)`, where `dtype` is a string Final outcome: - Frontend: astype(obj, new_dtype) - Backend: CastExpr(obj, new_dtype) ##### Steps - introduce builtin in field view and iterator IR - introduce builtin in `embedded` and `tracing` of iterator IR - extend `gtfn` code generator - needs to be tested at all levels #### Implement bitwise operators: Nikki (PR merged) Negation through the use of `tilde` (~) ```python= def bitwise_negation(cond: boolean, inp0: Field, inp1: Field): result = inp0 if ~cond else inp1 return result ``` Lower to `not`. Implementationg of XOR with use of `apostrophe` (^) ```python= def bitwise_xor(cond1: boolean, cond2: boolean, inp0: Field, inp1: Field): result = inp0 if cond1^cond2 else inp1 return result ``` Add xor operation in itir. #### More math functions - Nikki (PR merged) Added front-end tests Implement all functions from Cartesian GT4Py https://github.com/GridTools/gt4py/blob/adec0c3bf36fd2660ee766ec38114733672150ad/src/gt4py/gtscript.py#L39 #### index()-builtin Description: Add an `index(Dim)` builtin which returns the index of the current iteration point. ##### Syntax proposal ```python= @field_operator def foo(): return index(I) ``` returns a field of `I` indices from `start` to (including) `end-1` if the domain is `I:{start,end}`. ##### ITIR/backend implementation TODO: see other document, where the index iterator is introduced. #### Other features currently under development The following other features are already available as PRs: - scalar if - passing scalars to scan - calling functions from scan #### GT4Py features not targetted for this project Reductions in the vertical (aka "optimized returning CellDim field"). Workaround is to propagate the last level back (backward scan) or read the last level of a scan outside of the DSL. ### ICON Fortran integration Try if icon4pygen integration is feasible for this kind of program, otherwise use custom bindings generated similar to https://github.com/GridTools/gt4py/pull/608 (Hannes will support the implementation). ## Rabbit holes Potentially all new GT4Py features are blockers, therefore the implementation of the scheme should be pushed forward using available workarounds.