Functions

A function in Nutils is a mapping from a topology onto an n-dimensional array, and comes in the form of a functions: nutils.function.Array object. It is not to be confused with Python's own function objects, which operate on the space of general Python objects. Two examples of Nutils functions have already made the scene: the geometry geom, as returned by nutils.mesh.rectilinear, and the bases generated by Topology.basis(). Though seemingly different, these two constructs are members of the same class and in fact fully interoperable.

The nutils.function.Array functions behave very much like numpy.ndarray objects: the functions have a nutils.function.Array.shape, nutils.function.Array.ndim and a nutils.function.Array.dtype:

geom.shape
# (1,)
basis.shape
# (5,)
geom.ndim
# 1
geom.dtype
# <class 'float'>

The functions support numpy-style indexing. For example, to get the first element of the geometry geom you can write geom[0] and to select the first two basis functions you can write

myplot(topo, geom, basis[:2])

output

The usual unary and binary operators are available:

myplot(topo, geom, geom[0]*(1-geom[0])/2)

output

Several trigonometric functions are defined in the nutils.function module. An example with a sine function:

myplot(topo, geom, numpy.sin(2*geom[0]*numpy.pi))

output

The dot product is available via the '@' operator. To contract the basis with an arbitrary coefficient vector:

myplot(topo, geom, basis @ [1,2,0,5,4])

output

Recalling the definition of the discrete solution, the above is precisely the way to evaluate the resulting function. What remains now is to establish the coefficients for which this function solves the Laplace problem.

Arguments

A discrete model is often written in terms of (vectors of) unknowns. In Nutils, unknowns are represented by functions of type nutils.function.Argument, which have a known shape but undefined contents. Every argument has a name to identify it, which cannot be reused for different arguments in the same expression.

The discrete solution can now be written as an inner product with a basis, with the vector of unknowns represented by the argument 'u':

from nutils.function import Argument
u = basis @ Argument('u', basis.shape)

Since it is slightly tedious to create an argument that has a matching shape to an existing basis, the function module provides a convenience function that does the work for you.

from nutils.function import field
u = field('u', basis)

Moreover, as it is rarely necessary to manipulate a basis directly, the topology object adds a layer of convenience that creates the basis, contracts it with an argument, and returns only the result. This is the form that we will most often encounter:

u = topo.field('u', btype='spline', degree=1)

If a function depends on one or more arguments, then their values must be specified at evaluation time. However we cannot demonstrate that yet for the discrete solution defined here as it still requires a specification of the relevant topological points. This will be the subject of integrals.

What we can do is take derivatives with respect to arguments. The derivative appends the argument shape to that of the original function. Here we take the derivative of u, the function, with respect to 'u', the argument, to effectively extract the basis:

basis = u.derivative('u')