# underworld.function module¶

The function module contains the Function class, and related classes.

Function objects are constructed in python, but evaluated in C for efficiency. They provide a high level interface for users to compose model behaviour (such as viscosity), as well as a natural interface by which discrete data (such as meshvariables) may be utilised.

## Submodules¶

 underworld.function.analytic This module provides a suite of models which satisfy the Stokes system of equations. underworld.function.branching The branching module provides functions which provide branching behaviour. underworld.function.exception This module provides functions which raise an exception when given conditions are encountered during function evaluations. underworld.function.math This module provides math functions. underworld.function.misc Miscellaneous functions. underworld.function.rheology This module contains functions relating to rheological operations. underworld.function.shape This module includes shape type functions. underworld.function.tensor This module provides functions relating to tensor operations. underworld.function.view This module includes functions which provide views into the results of function queries.

## Classes¶

 underworld.function.Function Objects which inherit from this class provide user definable functions within Underworld. underworld.function.FunctionInput Objects that inherit from this class are able to act as inputs to function evaluation from python. underworld.function.coord alias of underworld.function._function.input underworld.function.input This class generates a function which simply passes through its input.
class underworld.function.Function(argument_fns, **kwargs)[source]

Bases: underworld._stgermain.LeftOverParamsChecker

Objects which inherit from this class provide user definable functions within Underworld.

Functions aim to achieve a number of goals: * Provide a natural interface for mathematical behaviour description within python. * Provide a high level interface to Underworld discrete objects. * Allow discrete objects to be used in combination with continuous objects. * Handle the evaluation of discrete objects in the most efficient manner. * Perform all heavy calculations at the C-level for efficiency. * Provide an interface for users to evaluate functions directly within python, utilising numpy arrays for input/output.

__add__(other)[source]

Fn3 = Fn1 + Fn2

Creates a new function Fn3 which performs additions of Fn1 and Fn2.

Examples

>>> import underworld.function.misc as misc
>>> import numpy as np
>>> three = misc.constant(3.)
>>> four  = misc.constant(4.)
>>> np.allclose( (three + four).evaluate(0.), [[ 7.]]  )  # note we can evaluate anywhere because it's a constant
True

__and__(other)[source]

Fn3 = Fn1 & Fn2

Creates a new function Fn3 which returns a bool result for the operation.

Returns: fn.logical_and AND function

Examples

>>> import underworld.function.misc as misc
>>> trueFn  = misc.constant(True)
>>> falseFn = misc.constant(False)
>>> (trueFn & falseFn).evaluate()
array([[False]], dtype=bool)


Notes

The ‘&’ operator in python is usually used for bitwise ‘and’ operations, with the ‘and’ operator used for boolean type operators. It is not possible to overload the ‘and’ operator in python, so instead the bitwise equivalent has been utilised.

__ge__(other)[source]

Fn3 = Fn1 >= Fn2

Creates a new function Fn3 which returns a bool result for the relation.

Returns: fn.greater_equal Greater than or equal to function

Examples

>>> import underworld.function.misc as misc
>>> import numpy as np
>>> two  = misc.constant(2.)
>>> (two >= two).evaluate()
array([[ True]], dtype=bool)

__getitem__(index)[source]

FnComponent = Fn[0]

Creates a new function FnComponent which returns the required component of Fn.

Returns: fn.at component function

Examples

>>> import underworld.function.misc as misc
>>> fn  = misc.constant((2.,3.,4.))
>>> np.allclose( fn[1].evaluate(0.), [[ 3.]]  )  # note we can evaluate anywhere because it's a constant
True

__gt__(other)[source]

Fn3 = Fn1 > Fn2

Creates a new function Fn3 which returns a bool result for the relation.

Returns: fn.greater Greater than function

Examples

>>> import underworld.function.misc as misc
>>> import numpy as np
>>> two  = misc.constant(2.)
>>> four = misc.constant(4.)
>>> (two > four).evaluate()
array([[False]], dtype=bool)

__le__(other)[source]

Fn3 = Fn1 <= Fn2

Creates a new function Fn3 which returns a bool result for the relation.

Returns: fn.less_equal Less than or equal to function

Examples

>>> import underworld.function.misc as misc
>>> import numpy as np
>>> two  = misc.constant(2.)
>>> (two <= two).evaluate()
array([[ True]], dtype=bool)

__lt__(other)[source]

Fn3 = Fn1 < Fn2

Creates a new function Fn3 which returns a bool result for the relation.

Returns: fn.less Less than function

Examples

>>> import underworld.function.misc as misc
>>> import numpy as np
>>> two  = misc.constant(2.)
>>> four = misc.constant(4.)
>>> (two < four).evaluate()
array([[ True]], dtype=bool)

__mul__(other)[source]

Fn3 = Fn1 * Fn2

Creates a new function Fn3 which returns the product of Fn1 and Fn2.

Returns: fn.multiply Multiply function

Examples

>>> import underworld.function.misc as misc
>>> import numpy as np
>>> three = misc.constant(3.)
>>> four  = misc.constant(4.)
>>> np.allclose( (three*four).evaluate(0.), [[ 12.]]  )  # note we can evaluate anywhere because it's a constant
True

__neg__()[source]

FnNeg = -Fn

Creates a new function FnNeg which is the negative of Fn.

Returns: fn.multiply Negative function

Examples

>>> import underworld.function.misc as misc
>>> import numpy as np
>>> four = misc.constant(4.)
>>> np.allclose( (-four).evaluate(0.), [[ -4.]]  )  # note we can evaluate anywhere because it's a constant
True

__or__(other)[source]

Fn3 = Fn1 | Fn2

Creates a new function Fn3 which returns a bool result for the operation.

Returns: fn.logical_or OR function

Examples

>>> import underworld.function.misc as misc
>>> trueFn  = misc.constant(True)
>>> falseFn = misc.constant(False)
>>> (trueFn | falseFn).evaluate()
array([[ True]], dtype=bool)


Notes

The ‘|’ operator in python is usually used for bitwise ‘or’ operations, with the ‘or’ operator used for boolean type operators. It is not possible to overload the ‘or’ operator in python, so instead the bitwise equivalent has been utilised.

__pow__(other)[source]

Fn3 = Fn1 ** Fn2

Creates a new function Fn3 which returns Fn1 to the power of Fn2.

Returns: fn.math.pow Power function

Examples

>>> import underworld.function.misc as misc
>>> import numpy as np
>>> two  = misc.constant(2.)
>>> four = misc.constant(4.)
>>> np.allclose( (two**four).evaluate(0.), [[ 16.]]  )  # note we can evaluate anywhere because it's a constant
True

__radd__(other)

Fn3 = Fn1 + Fn2

Creates a new function Fn3 which performs additions of Fn1 and Fn2.

Examples

>>> import underworld.function.misc as misc
>>> import numpy as np
>>> three = misc.constant(3.)
>>> four  = misc.constant(4.)
>>> np.allclose( (three + four).evaluate(0.), [[ 7.]]  )  # note we can evaluate anywhere because it's a constant
True

__rmul__(other)

Fn3 = Fn1 * Fn2

Creates a new function Fn3 which returns the product of Fn1 and Fn2.

Returns: fn.multiply Multiply function

Examples

>>> import underworld.function.misc as misc
>>> import numpy as np
>>> three = misc.constant(3.)
>>> four  = misc.constant(4.)
>>> np.allclose( (three*four).evaluate(0.), [[ 12.]]  )  # note we can evaluate anywhere because it's a constant
True

__rsub__(other)[source]

Fn3 = Fn1 - Fn2

Creates a new function Fn3 which performs subtraction of Fn2 from Fn1.

Returns: fn.subtract RHS subtract function

Examples

>>> import underworld.function.misc as misc
>>> import numpy as np
>>> four  = misc.constant(4.)
>>> np.allclose( (5. - four).evaluate(0.), [[ 1.]]  )  # note we can evaluate anywhere because it's a constant
True

__sub__(other)[source]

Fn3 = Fn1 - Fn2

Creates a new function Fn3 which performs subtraction of Fn2 from Fn1.

Returns: fn.subtract Subtract function

Examples

>>> import underworld.function.misc as misc
>>> import numpy as np
>>> three = misc.constant(3.)
>>> four  = misc.constant(4.)
>>> np.allclose( (three - four).evaluate(0.), [[ -1.]]  )  # note we can evaluate anywhere because it's a constant
True

__truediv__(other)[source]

Fn3 = Fn1 / Fn2

Creates a new function Fn3 which returns the quotient of Fn1 and Fn2.

Returns: fn.divide Divide function

Examples

>>> import underworld.function.misc as misc
>>> import numpy as np
>>> two  = misc.constant(2.)
>>> four = misc.constant(4.)
>>> np.allclose( (four/two).evaluate(0.), [[ 2.]]  )  # note we can evaluate anywhere because it's a constant
True

__xor__(other)[source]

Fn3 = Fn1 ^ Fn2

Creates a new function Fn3 which returns a bool result for the operation.

Returns: fn.logical_xor XOR function

Examples

>>> import underworld.function.misc as misc
>>> trueFn  = misc.constant(True)
>>> falseFn = misc.constant(False)
>>> (trueFn ^ falseFn).evaluate()
array([[ True]], dtype=bool)
>>> (trueFn ^ trueFn).evaluate()
array([[False]], dtype=bool)
>>> (falseFn ^ falseFn).evaluate()
array([[False]], dtype=bool)


Notes

The ‘^’ operator in python is usually used for bitwise ‘xor’ operations, however here we always use the logical version, with the operation inputs cast to their bool equivalents before the operation.

static convert(obj)[source]

This method will attempt to convert the provided input into an equivalent underworld function. If the provided input is already of Function type, it is immediately returned. Likewise, if the input is of None type, it is also returned.

Parameters: obj (fn_like) – The object to be converted. Note that if obj is of type None or Function, it is simply returned immediately. Where obj is of type int/float/double, a Constant type function is returned which evaluates to the provided object’s value. Where obj is of type list/tuple, a function will be returned which evaluates to a vector of the provided list/tuple’s values (where possible). Fn.Function or None.

Examples

>>> import underworld as uw
>>> import underworld.function as fn

>>> fn_const = fn.Function.convert( 3 )
>>> fn_const.evaluate(0.) # eval anywhere for constant
array([[3]], dtype=int32)

>>> fn_const == fn.Function.convert( fn_const )
True

>>> fn.Function.convert( None )

>>> fn1 = fn.input()
>>> fn2 = 10.*fn.input()
>>> fn3 = 100.*fn.input()
>>> vec = (fn1,fn2,fn3)
>>> fn_vec = fn.Function.convert(vec)
>>> fn_vec.evaluate([3.])
array([[   3.,   30.,  300.]])

evaluate(inputData=None, inputType=None)[source]

This method performs evaluate of a function at the given input(s).

It accepts floats, lists, tuples, numpy arrays, or any object which is of class FunctionInput. lists/tuples must contain floats only.

FunctionInput class objects are shortcuts to their underlying data, often with performance advantages, and sometimes they are the only valid input type (such as using Swarm objects as an inputs to SwarmVariable evaluation). Objects of class FeMesh, Swarm, FeMesh_IndexSet and VoronoiIntegrationSwarm are also of class FunctionInput. See the Function section of the user guide for more information.

Results are returned as numpy array.

Parameters: inputData (float, list, tuple, ndarray, underworld.function.FunctionInput) – The input to the function. The form of this input must be appropriate for the function being evaluated, or an exception will be thrown. Note that if no input is provided, function will be evaluated at 0. inputType (str) – Specifies the type the provided data represents. Acceptable values are ‘scalar’, ‘vector’, ‘symmetrictensor’, ‘tensor’, ‘array’. ndarray array of results

Examples

>>> import math as sysmath
>>> import underworld.function.math as fnmath
>>> sinfn = fnmath.sin()


Single evaluation:

>>> np.allclose( sinfn.evaluate(sysmath.pi/4.), [[ 0.5*sysmath.sqrt(2.)]]  )
True


Multiple evaluations

>>> input = (0.,sysmath.pi/4.,2.*sysmath.pi)
>>> np.allclose( sinfn.evaluate(input), [[ 0., 0.5*sysmath.sqrt(2.), 0.]]  )
True


Single MeshVariable evaluations

>>> mesh = uw.mesh.FeMesh_Cartesian()
>>> var = uw.mesh.MeshVariable(mesh,1)
>>> import numpy as np
>>> var.data[:,0] = np.linspace(0,1,len(var.data))
>>> result = var.evaluate( (0.2,0.5 ) )
>>> np.allclose( result, np.array([[ 0.45]]) )
True


Numpy input MeshVariable evaluation

>>> # evaluate at a set of locations.. provide these as a numpy array.
>>> count = 10
>>> # create an empty array
>>> locations = np.zeros( (count,2))
>>> # specify evaluation coodinates
>>> locations[:,0] = 0.5
>>> locations[:,1] = np.linspace(0.,1.,count)
>>> # evaluate
>>> result = var.evaluate(locations)
>>> np.allclose( result, np.array([[ 0.08333333],                                           [ 0.17592593],                                           [ 0.26851852],                                           [ 0.36111111],                                           [ 0.4537037 ],                                           [ 0.5462963 ],                                           [ 0.63888889],                                           [ 0.73148148],                                           [ 0.82407407],                                           [ 0.91666667]])  )
True


Using the mesh object as a FunctionInput

>>> np.allclose( var.evaluate(mesh), var.evaluate(mesh.data))
True


Also note that if evaluating across an empty input, an empty output is returned. Note that the shape and type of the output is always fixed and may differ from the shape/type returned for an actual (non-empty) evaluation. Usually this should not be an issue. >>> var.evaluate(np.zeros((0,2))) array([], shape=(0, 1), dtype=float64) >>> var.evaluate(mesh.specialSets[“Empty”]) array([], shape=(0, 1), dtype=float64)

evaluate_global(inputData, inputType=None)[source]

This method attempts to evalute inputData across all processes, and then consolide the results on the root processor. This is most useful where you wish to evalute your functions using global coordinates which may span processes in a parallel simulation.

Note that this method does not currently support ‘FunctionInput’ class input data.

Due to the communications required for this method, a significant performance overhead may be encountered. The standard evaluate method should be used instead wherever possible.

Please see evaluate method for parameter details.

Notes

This method must be called collectively by all processes.

Returns: Only the root process gets the final results array. All other processes are returned None.
integrate(mesh)[source]

Perform an integral of this underworld function over the given mesh

Parameters: mesh (uw.mesh.FeMesh_Cartesian) – Domain to perform integral over.

Examples

>>> mesh = uw.mesh.FeMesh_Cartesian(minCoord=(0.0,0.0), maxCoord=(1.0,2.0))
>>> fn_1 = uw.function.misc.constant(2.0)
>>> np.allclose( fn_1.integrate( mesh )[0], 4 )
True

>>> fn_2 = uw.function.misc.constant(2.0) * (0.5, 1.0)
>>> np.allclose( fn_2.integrate( mesh ), [2,4] )
True

class underworld.function.FunctionInput(*args, **kwargs)[source]

Bases: underworld._stgermain.LeftOverParamsChecker

Objects that inherit from this class are able to act as inputs to function evaluation from python.

underworld.function.coord

alias of underworld.function._function.input

class underworld.function.input(*args, **kwargs)[source]

Bases: underworld.function._function.Function

This class generates a function which simply passes through its input. It is the identity function. It is often useful when construct functions where the input itself needs to be accessed, such as to extract a particular component.

For example, you may wish to use this function when you wish to extract a particular coordinate component for manipulation. For this reason, we also provide an alias to this class called ‘coord’.

Returns: fn.input the input function

Examples

Here we see the input function simply passing through its input.

>>> infunc = input()
>>> np.allclose( infunc.evaluate( (1.,2.,3.) ), [ 1., 2., 3.] )
True


Often this behaviour is useful when we want to construct a function which operates on only a particular coordinate, such as a depth dependent density. We may wish to extract the z coordinate (in 2d):

>>> zcoord = input()[1]
>>> baseDensity = 1.
>>> density = baseDensity - 0.01*zcoord
>>> testCoord1 = (0.1,0.4)
>>> testCoord2 = (0.9,0.4)
>>> np.allclose( density.evaluate( testCoord1 ), density.evaluate( testCoord2 ) )
True