Language Reference¶
Abstract¶
This document contains a draft of the DSL syntax for GTScript on meshes.
Design Goals
Concise, compact and readable syntax
General enough to allow generation of efficient code for meshes with unstructured, partially structured, structured meshes
Easy to translate into GTScriptAST
Motivation and Scope¶
For global weather models (e.g. IFS-FVM, ICON) the ability to handle non-regular grids is required.
Concepts¶
This section is WIP and will be moved to the concepts repo when it matured.
Location
A bounded set with nonempty interior. We differentiate between horizontal locations, i.e. Cell, Edge, Vertex and vertical locations, i.e. a layer. Each location can be attributed to a domain dimesion.
Mesh
A subdivision of the computational domain into locations.
Field
A mapping between locations and quantities at this location.
Local field
A field defined in the neighbourhood of a cell.
Neighbourhood chain
Used to select direct or indirect neighbours of a cell. See here https://docs.google.com/document/d/1nQZzKGi7Go9R3fme78ViB_PrQDVPxy4xTzSuGh_yJAI/edit for a description.
Todo: properly define concept and add illustrations.
Primary location
The location(s) a stencil is applied to.
Secondary location
The location on which operations on neighbours act on.
Stencil¶
The entry point into GTScript code is a stencil, giving the user a local perspective on the mesh. The syntax to define a stencil follows the regular python syntax to define functions with an additional @gtscript.stencil decorator, where the first function argument is the Mesh followed by a set of Field s.
@gtscript.stencil
def stencil_name(mesh: Mesh, my_field : Field[[Vertex], dtype]):
# ...
Computations
The stencils body is composed of one or more computations specifying the iteration domain. The iteration domain is split into horizontal and vertical iteration domains. The vertical iteration domain, which is 1D, can have different policies: FORWARD, BACKWARD and PARALLEL, indicating the order constraints of the iteration. The horizontal iteration domain is of 2-dimensional nature with one unstructured dimension representing the geometrically 2-dimensional horizontal plane. There is only one horizontal iteration policy and it is always PARALLEL, so it is not specified. Statements are executed as specified in the GTScript parallel model
Computations are expressed (syntactically) using with statements and the three iteration specifications computation, interval and location.
computation(iteration order)Specifies the iteration order in the vertical dimension, where
iteration_policyis one of:PARALLEL,FORWARD,BACKWARDlocation(location_type)Restricts the horizontal dimension to locations of type
location_type.location(Vertex) location(Edge) location(Cell)
Locations may be labeled to reference them in neighbor reductions later on as follows:
with location(Vertex) as v: # ...
interval(start, end):Restricts the vertical dimension to the interval \([start, end[\), i.e. an interval including
startand excludingend.interval(0, 2) # layer 0 and 1 interval(0, -1) # all layers except for the last interval(-1, None) # only the last layer
The skeleton of a stencil my_stencil with a single field argument my_field defined on vertices, executing concurrently on all vertices, accross all layers then looks as follows:
@gtscript.stencil
def my_stencil(mesh: Mesh):
with computation(PARALLEL), location(Vertex), interval(0, None):
# ...
The iteration specifications may also be nested as long as their order is computation, location, interval.
@gtscript.stencil
def my_stencil(mesh: Mesh):
with computation(PARALLEL): # vertical iteration policy
with location(Vertex): # location specification
with interval(0, None): # vertical interval
# ...
A stencil running different computations on the first, last and the layers in between could then look as follows:
@gtscript.stencil
def my_stencil(mesh: Mesh):
with computation(PARALLEL): # vertical iteration policy
with location(Vertex): # location specification
with interval(0, 1):
# statements executed on the first layer
# ...
with interval(1, -1):
# statements executed on all, but the first and last layer
# ...
with interval(-1, None):
# statements executed on the last layer
# ...
The specification of the iteration policy and interval may also be skipped in which case the default iteration policy is PARALLEL and all layers are considered.
@gtscript.stencil
def my_stencil(mesh: Mesh):
with location(Vertex):
# ...
with location(Edge):
# ...
Todo: Incorporate sparse fields syntax
Types & Variables¶
GTScript only supports the following limited set of types. All variables are required to be of fixed type throughout the stencil.
Type |
Description |
|---|---|
|
A mapping between locations with their attributed dimensions |
|
A discrete representation of the computational domain (passed as first argument to the stencil call). |
|
The type of a location, i.e. |
|
A location, i.e. a specific cell, edge or vertex, where |
|
|
Variable types:
FieldTemporaryFieldLocationMesh
Todo: Expand & seperate variable and type documentation.
Statements¶
The only statements allowed are assignments.
Assignments¶
The left-hand-side of an assignment is always a Field defined on the current iteration space. If the field is not passed as a stencil argument, a temporary field is automatically introduced and may be referenced throughout the entire stencil. The right-hand-side of an assignment is an expression with type DataType.
field = expression
field[location] = expression
Modified example of the copy stencil emphasizing the behaviour of temporary fields:
@gtscript.stencil
def tmp_field_copy(
mesh: Mesh,
field_in : Field[[Vertex], float],
field_out : Field[[Vertex], float]
):
with location(Vertex) as v:
tmp_field[v] = field_in[v]
with location(Vertex) as v:
field_out[v] = tmp_field[v]
Example for a sparse field assignment using a generator expression on neighbors:
@gtscript.stencil
def scale_sparse(
mesh: Mesh,
original_field : SparseField[[Vertex], float]
scaled_field : SparseField[[Vertex], float]
):
with location(Vertex) as v:
scaled_field[v, :] = 2*original_field[v] for v in edges(v)
Todo: Off-center writes
Expressions¶
Literals / Constants¶
Only boolean and numeric literals are allowed. The precision of numeric literals is contrary to python by default machine-independent, but may be overridden by the user by specifying the dtype stencil decorator argument.
# booleans are always of type bool
True
False
# integer are of type dtypes["int"]
3
# float are of type dtypes["float"]
3.
The user may for example use 32 bit float and integer values for all literals of a stencil as follows:
@gtscript.stencil(dtypes={"float": np.float32, "int": np.int32})
def my_stencil(mesh: gtscript.Mesh):
with location(Vertex) as v:
my_field = my_field + 1.1 # 32 bit approximation of 1.1 used
my_field = my_field + 1 # 32 bit integer with value 1 used
The user may further explicitly specify the type of a literal using regular instantiation syntax, e.g. float32("1.1") for a float with 32 bits of precision.
# Integer
uint32("1")
uint64("1")
int32("1")
int64("1")
# Floating point
float32("1.1")
float64("1.1")
This allows for usage of literals with mixed precision.
@gtscript.stencil(dtypes={"float": np.float32})
def my_stencil(mesh: gtscript.Mesh):
with location(Vertex) as v:
my_field = my_field + 1.1 + float64("1.1")
Field access¶
Fields are accessed using the subscript operator [] with the index being the location to be accessed and a vertical offset. If no subscript is provided the value at the current location and layer is retrieved.
field # value at the current primary location and layer
field[v, 0] # value at the current layer and location `v`
field[v, -1] # value at location `v` with vertical offset -1
Arithmetic operators / Elementary reductions¶
Arithmetic operators and reductions on values of type gtc.common.DataType follow the regular python syntax.
a + b
a - b
a * b
a / b
min(a, b)
max(a, b)
Generator expressions on neighbors¶
Operations on neighbors are expressed using generator expressions, used e.g. in sparse field assignments and neighbor reductions. They follow the regular python syntax:
expression for location in neighbor_selector
where expression is just an expression, location the name of the symbol referencing the neighbors location and neighbor_selector is a neighbor selector. Inside the expression, fields may be referenced using location. Neighbors of the primary location can be selected via calls to the built-in function neighbors or one of the convenience functions vertices and edges.
# signature
neighbors(primary_location : Location, *chain : LocationType)
# select all cells sharing a common vertex with the current `cell`
neighbors(cell, Vertex, Cell)
Pseudo-code for vertices and edges convenience functions:
def vertices(of : Location):
return neighbors(of, Vertex)
def edges(of : Location):
return neighbors(of, Edge)
Example of a generator expression on the vertices of a primary location e:
vertex_field[v] for v in vertices(e)
Neighbor reductions¶
Reductions over neighbors are composed of a reduction function and a generator expression on the neighbors to reduce over. GTScript supports four reduction functions sum, product, min, max, computing the sum, product, mimimum and maximum, respectively, of its arguments.
Example computing the sum of vertex_field over all neighboring vertices of e:
sum(vertex_field[v] for v in vertices(e))
product(vertex_field[v] for v in vertices(e))
Optionally reductions may be supplied with a weights argument, where the i-th weight is multiplied to the value on the i-th neighbor.
sum(gen_exp, weights=None)
product(gen_exp, weights=None)
min(gen_exp, weights=None)
max(gen_exp, weights=None)
Todo: Sparse field example