---
# System prepended metadata

title: Frontend workshop MCH 2021.10.20
tags: [shaping, frontend]

---

###### tags: `frontend`, `shaping`

# Frontend workshop MCH 2021.10.20

## Workshop Conclusions

- The general idea of the frontend allows mixing local and field views.
- It looks like it can work to implement ICON dycore in the next couple of years, although some design choices and several syntax details are still open.
- There is some open discussion (specially for MCH) about introducing a fake`if-else` ([here](#Ifelse-vs-ternary)) construct:
    - Ben's view: which _could_ improve readability for some users, but it will have non-standard and slightly ambiguous semantics, and it will not allow debugability.
    - Hannes' view: because on a field level there is no semantically meaningful if/else.

---------------------------------------


## Meeting 19.10.21 minutes

### Agenda
- Who do we target?
- Current state of frontend
- MCH list of discussion points
- Look at weird ICON stencils

### General discussion topics

- MCH needs stencil-to-stencil translation from ICON to the DSL
- CSCS/EXCLAIM most likely will develop standalone Python driver code on top of the stencils from the beginning


### Who do we target?

- Software engineers
- Domain scientists with interest in our DSL (people close to us)
- DWD Fortran programmers?
- Students / PhDs working in EXCLAIM?

### Connectivities vs Neighborhood chains

There is no blocker, we can represent Neighborhood chains as annotations or relevant transformations outside of IR.

If we like we can use the dusk notation for offsets "Edge > Cell > Vertex".

### If/else vs ternary

We discussed 2 ways to make local ifs work, both have weird rules:
1. we deref always (and if you want to keep the iterator you lift (but this is maybe not a use-case))
2. we deref explicitly, but you cannot have iterators as the stores in an if

**Ben's proposal**

Everyting is derefed on first access.

```python=
def conditional_return_value(cond: iterator, a: iterator, b: iterator):
    if cond:
        tmp = a # weird tmp is value, a is iterator
    else:
        tmp = b
    return tmp # value, cannot shift here
```
```python=
def conditional_return_iterator(cond: iterator, a: iterator, b: iterator):
    if cond:
        tmp = lift(lambda x: x)(a) # or shortcut Field(a)
    else:
        tmp = lift(lambda x: x)(b)
    return tmp[I+1] # possible
```
We can easily construct the second case, but probably that is not a use-case. Also it doesn't work for embedded.

**Beautified iterator**
```python=
def conditional(cond: iterator, a: iterator, b: iterator):
    if cond():
        tmp = a # tmp is an iterator
    else:
        tmp = b
    # oops, we cannot transform to
    # if_(cond(), a, b) because if_ only takes values
    return tmp # value, cannot shift here
```
Note, that the restriction only excludes a case that we probably don't have (return a full iterator depending on a value).

**Lazy field** 
```python=
def conditional(cond: field, a: field, b: field):
    if cond:
        tmp = a # tmp is a field
    else:
        tmp = b
    # oops, we return either a, or b, but probably we want np.where 
    return tmp
```
This cannot be executed embedded and is weird because it behaves not in the Python way.

**Conclusion**
If we want embedded we can only do beautified iterator.



## Meeting 20.10.21

### Agenda

- ICON examples (stencils and driver code)
- Shape inference discussions
- Sparse fields inside the stencils?
- Timeline / roadmap ?


### ICON examples (stencils and driver code)
```python=
# Stencils
def nabv_ref(
    weights: Field[Diamond],
    u_vert: Field[K, Vertex],
    v_vert: Field[K, Vertex],
    primal_normal_vert_v1: Field[Edge, Diamond],
    primal_normal_vert_v2: Field[Edge, Diamond],
    mask: Field[K],
) -> Field[Edge]:
    return gt.where(
        mask & mask(K+1),
        gt.sum(
            (
                u_vert[diamond_conn] * primal_normal_vert_v1
                + v_vert[diamond_conn] * primal_normal_vert_v2
            )
            * weights,
            axis=Diamond,
        ),
        0,
    )

def nabv_ref(
    weights: Field[Diamond],
    u_vert: Field[K, Vertex],
    v_vert: Field[K, Vertex],
    primal_normal_vert_v1: Field[Edge, Diamond],
    primal_normal_vert_v2: Field[Edge, Diamond],
    mask: Field[K],
) -> Field[Edge]:
    return gt.sum(
            (
                u_vert[diamond_conn] * primal_normal_vert_v1
                + v_vert[diamond_conn] * primal_normal_vert_v2
            )
            * weights,
            axis=Diamond,
        )


def level_mask(mask, inp):
    return where(mask, inp, 0)

def nabv_ref_masked(mask, ...):
    return level_mask(mask&mask(K+1), nabv_ref(...))

def nabv_tang_ref(u_vert, v_vert, primal_normal_vert_v1, primal_normal_vert_v2):
    weights = np.asarray([[1.0, 1.0, 0.0, 0.0]] * n_edges)
    print(u_vert)
    return nabv_ref(
        weights, u_vert, v_vert, primal_normal_vert_v1, primal_normal_vert_v2
    )


def nabv_norm_ref(u_vert, v_vert, primal_normal_vert_v1, primal_normal_vert_v2):
    weights = np.asarray([[0.0, 0.0, 1.0, 1.0]] * n_edges)
    return nabv_ref(
        weights, u_vert, v_vert, primal_normal_vert_v1, primal_normal_vert_v2
    )


def z_nabla4_e2_ref(
    nabv_norm, nabv_tang, z_nabla2_e, inv_vert_vert_length, inv_primal_edge_length
):
    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
    )
```

### Shape inference 

- For ICON, it should be possible to do backward shape inference (from output domain to input domains) in the _spatially sorted_ part of the domain.


ICON Mesh information (from: [ICON Model Tutorial 2020 (page 167)](https://www.dwd.de/EN/ourservices/nwv_icon_tutorial/pdf_volume/icon_tutorial2020_en.pdf))

![](https://i.imgur.com/2RlSdI9.png)


### Sparse fields inside the stencils

- TODO: Check with Anton