32.5. Best Practices for Complex User Defined Functions

User defined functions (UDFs) can be complex. For instance, a material parameter or a boundary condition can be given as the a map of values on a regular grid and those data can be used by several UDFs, potentially involving many values. Creating, as well as calculating, such UDFs can be time consuming however, there are some recommended guidelines for setting up such UDFs and reducing their runtime.


Note:  This section particularly focuses on PMAT-based UDFs because they are called very often, and at all integration points of each element.


Here are some recommended best practices for complex UDFs in order to:

  • Avoid time consuming statements.

    As explained in Solver Performance with UDFs, using CLIPS can be time consuming and some statements may increase the CPU dramatically. A function such as (create$ 1 2 3 4), used to create a set of values, is time consuming when there are many values and if the function is executed for each UDF call. Using a global variable to store the set of values may avoid this drawback.

  • Use sub-functions for simplicity and reusability.

    For complex UDFs or for several UDFs using the same logic, it is more convenient to split the function(s) into simpler sub-functions.

  • Use global variables shared between the UDF and their sub-functions.

    A global variable is useful to store constant data because its statement is executed only once during the reading of the UDF file. Moreover, it is also useful for variables used by different CLIPS sub-functions called by a UDF because it does not need to transfer them via argument or returned values (which can be a complicated operation in CLIPS). For parallelization reasons, a global variable (G) is shared between the sub-functions called by a UDF and this UDF (such that the value of G is ensured to be the same between the sub-functions called by the UDF and this UDF). The value of G however, is not ensured to be the same when several UDFs are involved.

    See An Example of a Complex UDF for an illustration.

  • Return error code(s) for the UDF if something unexpected occurs.

    It is useful to return error codes from the UDF if the UDF is called with an unexpected value of an argument due to a divergence of the iterative scheme (this feature is available since version 21.2). If a UDF calls the square root method sqrt(A), it is mandatory that A is greater than 0. If this kind of error occurs and if the user does not detect it, CLIPS will print a message of the form:

    [EMATHFUN1] Domain error for sqrt function.
    [PRCCODE4] Execution halted during the actions of defunction ‘abc’

    Such CLIPS messages are not always clear or informative, so, if this occurs,it is preferable to provide the user with their own message. That way, the user can check such situations, print an error message with relevant information and return an error code. This error code cannot be a numerical value. Note the value is not relevant, only the type is; so one can suggest to return T (True) if an error occurs.

This section describes some issues related to complex UDFs, including an example and a sample of a CLIPS file.