underworld.function.branching module

The branching module provides functions which provide branching behaviour. Typically, these functions will select other user provided functions when certain conditions are met (with the condition also described by a function!).


underworld.function.branching.conditional This function provides ‘if/elif’ type conditional behaviour.
underworld.function.branching.map This function performs a map to other functions.
class underworld.function.branching.conditional(clauses, *args, **kwargs)[source]

Bases: underworld.function._function.Function

This function provides ‘if/elif’ type conditional behaviour.

The user provides a list of tuples, with each tuple being of the form (fn_condition, fn_resultant). Effectively, each tuple provides a clause within the if/elif statement.

When evaluated, the function traverses the clauses, stopping at the first fn_condition which returns ‘true’. It then executes the corresponding fn_resultant and returns the results.

If none of the provided clauses return a ‘True’ result, an exception is raised.

For a set of condition functions { fc_0, fc_1, … ,fc_n }, and corresponding resultant functions { fr_0, fr_1, … ,fr_n }, we have for a provided input f_in:

if   fc_0(f_in) :
    return fr_0(f_in)
elif fc_1(f_in) :
    return fr_1(f_in)
elif fc_n(f_in) :
    return fr_n(f_in)
else :
    raise RuntimeError("Reached end of conditional statement. At least one
                        of the clause conditions must evaluate to 'True'." );
Parameters:clauses (list) – list of tuples, with each tuple being of the form (fn_condition, fn_resultant).


The following example uses functions to represent a unit circle. Here a conditional function report back the value ‘1.’ inside the sphere (as per the first condition), and ‘0.’ otherwise.

>>> import underworld as uw
>>> import underworld.function as fn
>>> import numpy as np
>>> mesh = uw.mesh.FeMesh_Cartesian(elementRes=(16,16),minCoord=(-1.0, -1.0), maxCoord=(1.0, 1.0))
>>> circleFn = fn.coord()[0]**2 + fn.coord()[1]**2
>>> fn_conditional = fn.branching.conditional( [ (circleFn < 1., 1. ),                                                      (         True, 0. ) ] )
>>> np.allclose(np.pi, uw.utils.Integral(fn_conditional,mesh).evaluate(),rtol=1e-2)
class underworld.function.branching.map(fn_key=None, mapping=None, fn_default=None, *args, **kwargs)[source]

Bases: underworld.function._function.Function

This function performs a map to other functions. The user provides a python dictionary which maps unsigned integers keys to underworld functions. The user must also provide a key function. At evaluation time, the key function is evaluated first, with the outcome determining which function should finally be evaluated to return a value.

For a set of value functions \(\{f_{v_0},f_{v_1},\ldots,f_{v_n}\}\), corresponding keys \(\{k_0,k_1,\ldots,k_n\}\), and key function \(f_{k}\), we have:

\[\begin{split}f(\mathbf{r})= \begin{cases} f_{v_0}(\mathbf{r}), & \text{if } f_{k}(\mathbf{r}) = k_0\\ f_{v_1}(\mathbf{r}), & \text{if } f_{k}(\mathbf{r}) = k_1\\ ... \\ f_{v_n}(\mathbf{r}), & \text{if } f_{k}(\mathbf{r}) = k_n\\ f_{d} (\mathbf{r}), & \text{otherwise} \end{cases}\end{split}\]

As stated, the keys must be unsigned integers. The key function need not return an unsigned integer, but whatever value it returns will be cast to an unsigned integer so caution is advised.

The default function is optional, but if none is provided, and the key function evaluates to a value which is not within the user provide set of keys, an exception will be thrown.

  • fn_key (underworld.function.Function (or convertible)) – Function which returns integer key values. This function will be evaluated first to determine which function from the mapping is to be used.
  • mapping (dict(Function)) – Python dictionary providing a mapping from unsigned integer ‘key’ values to underworld ‘value’ functions. Note that the provided ‘value’ functions must return values of type ‘double’.
  • fn_default (underworld.function.Function (or convertible) (optional)) – Default function to be utilised when the key (returned by fn_key function) does not correspond to any key value in the mapping dictionary.

The following example sets different function behaviour inside and outside of a unit sphere. The unit sphere is represented by particles which record a swarm variable to determine if they are or not inside the sphere.


Setup mesh, swarm, swarmvariable & populate

>>> import underworld as uw
>>> import underworld.function as fn
>>> import numpy as np
>>> mesh = uw.mesh.FeMesh_Cartesian(elementRes=(8,8),minCoord=(-1.0, -1.0), maxCoord=(1.0, 1.0))
>>> swarm = uw.swarm.Swarm(mesh)
>>> svar = swarm.add_variable("int",1)
>>> swarm.populate_using_layout(uw.swarm.layouts.PerCellSpaceFillerLayout(swarm,20))

For all particles in unit circle, set svar to 1

>>> svar.data[:] = 0
>>> for index, position in enumerate(swarm.particleCoordinates.data):
...     if position[0]**2 + position[1]**2 < 1.:
...         svar.data[index] = 1

Create a function which reports the value ‘1.’ inside the sphere, and ‘0.’ otherwise. Note that while we have only used constant value functions here, you can use any object of the class Function.

>>> fn_map = fn.branching.map(fn_key=svar, mapping={0: 0., 1:1.})
>>> np.allclose(np.pi, uw.utils.Integral(fn_map,mesh).evaluate(),rtol=2e-2)

Alternatively, we could utilise the default function to achieve the same result.

>>> fn_map = fn.branching.map(fn_key=svar, mapping={1: 1.}, fn_default=0.)
>>> np.allclose(np.pi, uw.utils.Integral(fn_map,mesh).evaluate(),rtol=2e-2)