# ICON stencils in field view examples
## Diffusion stencil 5
### Dusk version
```python=
from dusk.script import *
lb, nudging, interior, halo, end = HorizontalDomains(0, 1000, 2000, 3000, 4000)
@stencil
def mo_nh_diffusion_stencil_05(
u_vert: Field[Vertex, K],
v_vert: Field[Vertex, K],
primal_normal_vert_v1: Field[Edge > Cell > Vertex],
primal_normal_vert_v2: Field[Edge > Cell > Vertex],
z_nabla2_e: Field[Edge, K],
inv_vert_vert_length: Field[Edge],
inv_primal_edge_length: Field[Edge],
z_nabla4_e2: Field[Edge, K]
):
nabv_tang: Field[Edge, K]
nabv_norm: Field[Edge, K]
with domain.upward.across[nudging:halo]:
nabv_tang = sum_over(
Edge > Cell > Vertex,
u_vert * primal_normal_vert_v1 + v_vert * primal_normal_vert_v2,
weights=[1.0, 1.0, 0.0, 0.0],
)
nabv_norm = sum_over(
Edge > Cell > Vertex,
u_vert * primal_normal_vert_v1 + v_vert * primal_normal_vert_v2,
weights=[0.0, 0.0, 1.0, 1.0],
)
z_nabla4_e2 = 4.0 * (
(nabv_norm - 2.0 * z_nabla2_e) * inv_vert_vert_length ** 2
+ (nabv_tang - 2.0 * z_nabla2_e) * inv_primal_edge_length ** 2
)
```
### Field view with functions
```python=
import gt4py as gt
domain = {K: range(0,65), Edge: range(nudging, halo)}
@stencil
def vn_vert(
weights: Field[Diamond],
u_vert: Field[Vertex, K],
v_vert: Field[Vertex, K],
primal_normal_vert_v1: Field[Edge, Diamond],
primal_normal_vert_v2: Field[Edge, Diamond]
) -> Field[Edge, Diamond]:
return gt.sum(
(
u_vert(Diamond) * primal_normal_vert_v1
+ v_vert(Diamond) * primal_normal_vert_v2
)
* weights, # TODO need to discuss if we implicitly broadcast here or not
axis=Diamond,
)
@stencil
def nabv_tang(
u_vert: Field[Vertex, K],
v_vert: Field[Vertex, K],
primal_normal_vert_v1: Field[Edge, Diamond],
primal_normal_vert_v2: Field[Edge, Diamond]
) -> Field[Edge, K]:
weights: Field[Diamond] = gt.weights([[1.0, 1.0, 0.0, 0.0]])
return vn_vert(weights, u_vert, v_vert, primal_normal_vert_v1, primal_normal_vert_v2)
@stencil
def nabv_norm(
u_vert: Field[Vertex, K],
v_vert: Field[Vertex, K],
primal_normal_vert_v1: Field[Edge, Diamond],
primal_normal_vert_v2: Field[Edge, Diamond]
) -> Field[Edge, K]:
weights: Field[Diamond] = gt.weights([[0.0, 0.0, 1.0, 1.0]])
return vn_vert(weights, u_vert, v_vert, primal_normal_vert_v1, primal_normal_vert_v2)
@stencil
def z_nabla4_e2(
nabv_norm: Field[Edge, K],
nabv_tang: Field[Edge, K],
z_nabla2_e: Field[Edge, K],
inv_vert_vert_length: Field[Edge],
inv_primal_edge_length: Field[Edge]
) -> Field[Edge, K]:
return 4.0 * (
(nabv_norm - 2.0 * z_nabla2_e) * inv_vert_vert_length ** 2
+ (nabv_tang - 2.0 * z_nabla2_e) * inv_primal_edge_length ** 2
)
@fencil
def mo_nh_diffusion_stencil_05(
u_vert: Field[Vertex, K],
v_vert: Field[Vertex, K],
primal_normal_vert_v1: Field[Edge, Diamond],
primal_normal_vert_v2: Field[Edge, Diamond],
z_nabla2_e: Field[Edge, K],
inv_vert_vert_length: Field[Edge],
inv_primal_edge_length: Field[Edge],
z_nabla4_e2: Field[Edge, K]
) -> Field[Edge, K]:
nabv_tang = nabv_tang(u_vert, v_vert, primal_normal_vert_v1, primal_normal_vert_v2)
nabv_norm = nabv_norm(u_vert, v_vert, primal_normal_vert_v1, primal_normal_vert_v2)
z_nabla4_e2 = z_nabla4_e2(
nabv_norm, nabv_tang, z_nabla2_e, inv_vert_vert_length, inv_primal_edge_length
)
return z_nabla4_e2[domain]
```
### Field view closest to dusk
```python=
Diamond = gt.ICON.nbhdChain(Edge, Cell, Vertex)
# Hannes: currently
Diamond = offset("Diamond")
@stencil
def mo_nh_diffusion_stencil_05(
u_vert: Field[Vertex, K],
v_vert: Field[Vertex, K],
primal_normal_vert_v1: Field[Diamond],
primal_normal_vert_v2: Field[Diamond],
z_nabla2_e: Field[Edge, K],
inv_vert_vert_length: Field[Edge],
inv_primal_edge_length: Field[Edge],
):
nabv_tang = sum(
(
u_vert(Diamond) * primal_normal_vert_v1
+ v_vert(Diamond) * primal_normal_vert_v2
)
* Field(Diamond)([1.0, 1.0, 0.0, 0.0]),
axis=Diamond,
)
nabv_norm = sum(
(
u_vert(Diamond) * primal_normal_vert_v1
+ v_vert(Diamond) * primal_normal_vert_v2
)
* (Field(Diamond)([0.0, 0.0, 1.0, 1.0])),
axis=Diamond,
)
z_nabla4_e2 = 4.0 * (
(nabv_norm - 2.0 * z_nabla2_e) * inv_vert_vert_length ** 2
+ (nabv_tang - 2.0 * z_nabla2_e) * inv_primal_edge_length ** 2
)
return z_nabla4_e2
def main(
k_size,
nudging,
halo,
u_vert,
v_vert,
primal_normal_vert_v1,
primal_normal_vert_v2,
z_nabla2_e,
inv_vert_vert_length,
inv_primal_edge_length,
z_nabla4_e2,
):
domain = {K: range(0, k_size), Edge: range(nudging, halo)}
mo_nh_diffusion_stencil_05[domain](
u_vert,
v_vert,
primal_normal_vert_v1,
primal_normal_vert_v2,
z_nabla2_e,
inv_vert_vert_length,
inv_primal_edge_length,
out=z_nabla4_e2,
)
```
#### Alternative reduction syntax
This is a sketch and we can discuss if we can improve.
```python=
nabv_tang = sum(
(
u_vert(v) * primal_normal_vert_v1[i]
+ v_vert(v) * primal_normal_vert_v2[i]
)
* [1.0, 1.0, 0.0, 0.0][i] for i, v in enumerate(EdgeCellVertex)
)
```
## Diffusion stencil 14
### Dusk version
```python=
from dusk.script import *
lb, nudging, interior, halo, end = HorizontalDomains(0, 1000, 2000, 3000, 4000)
thresh_tdiff = Global("thresh_tdiff")
@stencil
def mo_nh_diffusion_stencil_14(
theta_v: Field[Cell, K],
theta_ref_mc: Field[Cell, K],
enh_diffu_3d: Field[Cell, K],
):
tdiff: Field[Cell, K]
trefdiff: Field[Cell, K]
with domain.upward[-2:].across[nudging-1:halo-1]:
tdiff = theta_v - sum_over(Cell > Edge > Cell, theta_v[Cell > Edge > Cell]) / 3
trefdiff = (
theta_ref_mc
- sum_over(Cell > Edge > Cell, theta_ref_mc[Cell > Edge > Cell]) / 3
)
if (
tdiff - trefdiff < thresh_tdiff
and trefdiff < 0
# or tdiff - trefdiff < 1.5 * thresh_tdiff MR: can't find this conditional in the ICON code!?
):
enh_diffu_3d = (thresh_tdiff - tdiff + trefdiff) * 5e-4
else:
enh_diffu_3d = -1.7976931348623157e+308
```
### Field view closest to dusk
As a test use full type notation here, including `float`.
```python=
Triforce = gt.ICON.nbhdChain(Cell, Edge, Cell)
# Hannes: currently
Triforce = offset("Triforce")
@stencil
def mo_nh_diffusion_stencil_14(
theta_v: Field[[Cell, K], float],
theta_ref_mc: Field[[Cell, K], float],
thresh_tdiff: float,
enh_diffu_3d: Field[[Cell, K], float],
):
tdiff = theta_v - sum(theta_v[Triforce], axis=Triforce) / 3
trefdiff = theta_ref_mc - sum(theta_ref_mc[Triforce], axis=Triforce) / 3
enh_diff_3d = gt.ternary(
tdiff - trefdiff < thresh_tdiff & trefdiff < 0,
(thresh_tdiff - tdiff + trefdiff) * 5e-4,
-1.7976931348623157e+308,
)
return enh_diffu_3d
def main(
k_size,
nudging,
halo,
theta_v,
theta_ref_mc,
thresh_tdiff,
enh_diffu_3d,
):
domain = {K: range(k_size - 2, k_size), Cell: range(nudging - 1, halo - 1)}
mo_nh_diffusion_stencil_14[domain](
theta_v,
theta_ref_mc,
thresh_tdiff,
out=enh_diffu_3d,
)
```
## Diffusion stencil 18
### Dusk version
```python=
from dusk.script import *
from dusk.test import stencil_test
lb, nudging, interior, halo, end = HorizontalDomains(0, 1000, 2000, 3000, 4000)
@stencil
def mo_nh_diffusion_stencil_18(
# This mask field is not in the original Fortran, since it builds a generic index list that contains
# the result of masking the entire domain
mask: Field[Cell, K],
zd_vertidx: IndexField[Origin+Cell > Edge > Cell, K],
zd_vertidx_p1: IndexField[Origin+Cell > Edge > Cell, K],
zd_diffcoef: Field[Cell, K],
geofac_n2s: Field[Origin + Cell > Edge > Cell],
# In fortran vcoef does not contain center. It will require copying it into a new sparse field
# that contains center with value 1, before calling codegen.
vcoef: Field[Origin+Cell > Edge > Cell, K],
theta_v: Field[Cell, K],
z_temp: Field[Cell, K]):
with domain.upward.across[nudging:end]:
if(mask):
z_temp += zd_diffcoef * \
sum_over(Origin+Cell > Edge > Cell, geofac_n2s *
(vcoef*theta_v[Origin+Cell > Edge > Cell, zd_vertidx] +
(1-vcoef)*theta_v[Origin+Cell > Edge > Cell, zd_vertidx_p1 + 1]))
```
### Field view closest to dusk
As a test use full type notation here, including `float`.
```python=
Triforce = gt.ICON.nbhdChain(Cell, Edge, Cell)
# Hannes: currently
Triforce = offset("Triforce")
@stencil
def mo_nh_diffusion_stencil_18(
mask: Field[[Cell, K], bool],
zd_vertidx: Field[[Triforce, K], int],
zd_vertidx_p1: Field[[Triforce, K], int],
zd_diffcoef: Field[[Cell, K], float],
geofac_n2s: Field[[Triforce], float],
vcoef: Field[[Triforce, K], float],
theta_v: Field[[Cell, K], float],
z_temp: Field[[Cell, K], float],
):
z_temp = gt.where(
mask,
z_temp +
zd_diffcoef *
sum(
geofac_n2s * (vcoef * theta_v[Triforce, zd_vertidx] + (1 - vcoef) * theta_v[Triforce, zd_vertidx_p1 + 1]),
axis=Triforce
),
z_temp
)
return z_temp
def main(
k_size,
nudging,
end,
mask,
zd_vertidx,
zd_vertidx_p1,
zd_diffcoef,
geofac_n2s,
vcoef,
theta_v,
z_temp,
):
domain = {K: range(0, k_size), Cell: range(nudging, end)}
mo_nh_diffusion_stencil_18[domain](
mask,
zd_vertidx,
zd_vertidx_p1,
zd_diffcoef,
geofac_n2s,
vcoef,
theta_v,
out=z_temp,
)
```