User-Written Functions
User-written functions are functions you can create yourself. There are two types of user-written functions: interpreted and compiled.
The following sections explain the user-written functions:
Interpreted Functions
Interpreted functions consist of text inserted into an expression when Adams evaluates the expression. You can create these functions in the command window, using the FUNCTION command. When you create them, you must specify the text of the function and the parameter names. When you use these functions, Adams substitutes the user parameters into the function text in place of the parameter names.
For example:
function create function_name = MID_PT &
text_of_expression = "LOC_ALONG_LINE(P1,P2,DM(P1,P2)/2)" &
argument_names = "P1", "P2" &
type = location_orientation
In the example above, P1 and P2 are the formal arguments to the function MID_PT.
In the following example, Adams positions marker_3 half way between marker_1 and marker_ 2:
marker create marker_name = marker_3 location=(MID_PT(marker_1, marker_2))
Compiled Functions
You can also write compiled functions in C or FORTRAN, link them into Adams View, and even use them in an Adams View expression. You can use these functions in the same way you would use the built-in functions.
Register the user-written functions by calling a subroutine built in to Adams View. You must place this subroutine call in the registration subroutine supplied in source-code form with Adams View.
To create your own compiled function:
1. Locate and copy the appropriate source code templates from $topdir/aview/user_subs. If you are programming in C, copy vc_init_usr.c. If you are programming in FORTRAN, copy vf_init_usr.f.
2. Write and debug your function by modifying the template. You can debug by compiling your function in debug mode, linking the executable in debug mode and using your native debugger.
3. Add your new function to the registration subroutine.
4. Link your new function and the modified registration subroutine with Adams View. Depending on the platform you're on, look for instructions in the
Running and Configuring Adams help.
5. Use your new function in an expression.
You can use the programming language's normal parameter passing method to access parameters. Adams allows you to use this mechanism only for a fixed set of parameter lists and return values. You can find the set of allowable parameter lists in the files $topdir/aview/user_subs/mdi_c.h and $topdir/aview/user_subs/mdi_f.f.
Registering functions allows you to add functions with arbitrary names. User-written functions have precedence over system-supplied functions. If you register a function with the name SQRT, then your new function is called whenever you use this name in an expression. You can't register a function that has the same name as an existing literal constant. For example, Adams View rejects names such as PI and RTOD, and returns an error message.
If you have a function that duplicates a constant name, change the text name as shown next:
vc_function_add("RTOD", (FUNCTION)rtod, fn_R_RR, 2, 0); /* Won't work */
to
vc_function_add("MYRTOD", (FUNCTION)rtod, fn_R_RR, 2, 0); /* Will work */
When you register a function you must also specify the number and type of parameters, to give the expression parser the information it needs to make sure the input and output data for the function are correct.
Examples Involving Compiled Functions
The following examples show how you can write your own compiled functions and how you can access arrays within compiled functions:
Writing Your Own Compiled Functions
This example adds a function that computes the remainder between two reals. When the function is registered in Adams View, its type, and the number and types of its parameters are defined, so the parser can validate the input and output data.
Your code might look like this:
#include "mdi_c.h" /* This file is in $topdir/aview/user_subs */
#include <math.h>
typedef double REAL;
REAL remainder (REAL number, REAL denom)
{
int i;
return(number - (i * denom) );
}
#inlcude "mdi_vc.h"
void vc_initialize_user ()
}
REAL remainder ();
vc_function_add("REMAINDER",(FUNCTION) remainder, fn_fn_R_RR, 2, 0) ;
}
You must register the function in Adams View. Do this by modifying a function that is supplied in source code form (in the file $topdir/aview/user_subs/vc_init_usr.c), compiling and linking it with Adams View, as shown next:
#include "mdi_vc.h"
void vc_initialize_user ( )
{
REAL dist2 ( ) ;
vc_function_add("DIST2", (FUNCTION)dist2, fn_R_RR, 2, 0);
}
To use this function in an expression in Adams View, type:
variable set variable=myvar real=(EVAL(REMAINDER(101.0/17.0)))
If you perform an error check within your user-written function, you can cause the function to abort and report an error message by using the following routine:
void vc_error(char *Msg, ...);
The Msg parameter is a C format, and the optional parameters are the same as with a printf. Note that this function does not return to the calling routine.
Accessing Arrays Within Compiled Functions
In the Adams View expression language, an array is a collection of values of the same scalar type. For more information on arrays, see
Arrays.
The following C prototypes define the functional interface to the array object:
#define vc_MAX_RANK 7 /* Only 2 are usable in this release. */
typedef int vc_DIMS[vc_MAX_RANK];
typedef int vc_BNDS[vc_MAX_RANK][2];
typedef void *vc_ARRAY
;typedef int BOOL; /* True or false values */
typedef double REAL;
vc_ARRAY *vc_array_create(vc_DIMS Dims, int Rank, BOOL ColumnVector);
void vc_array_set_values(vc_ARRAY *A, REAL *Values, int Index, int Count);
void vc_array_get_values(vc_ARRAY *A, REAL *Values, int Index, int Count);
REAL *vc_array_values (vc_ARRAY *A);
void vc_array_set_dims(vc_ARRAY *A, vc_DIMS Dims, int Rank, BOOL ColumnVector);
void vc_array_get_dims(vc_ARRAY *A, vc_DIMS Dims, int *Rank, BOOL *ColumnVector);
int vc_array_compute_index(vc_ARRAY *A, vc_BNDS Bounds, int nBounds);
void vc_array_form_submatrix(vc_ARRAY *A, vc_BNDS Bounds, int nIndex);
int vc_array_element_count(vc_ARRAY *A);
BOOL vc_array_same_shape(vc_ARRAY *A1, vc_ARRAY *A2);
vc_EL vc_array_elements (vc_ARRAY A);
void vc_array_set_element_type (vc_ARRAY A, EXPR_TYPE ElementType);
EXPR_TYPE vc_array_element_type (vc_ARRAY A);
void vc_array_coerce_element_type (vc_ARRAY A, EXPR_TYPE ElementType);
void vc_array_set_string_element (vc_ARRAY A, char *Value, int Index);
int vc_array_source_object (vc_ARRAY A);
The source code for the
DMAT function is given below as an example of how to use this interface:
vc_ARRAY vc_dmat(vc_ARRAY A)
{
vc_ARRAY NewArray;
vc_DIMS Dims;
int Size;
int Rank;
BOOL ColV;
REAL *Values;
int i;
vc_array_get_dims(A, Dims, &Rank, &ColV);
/* Verify the shape of the input array */
if ( Rank > 2 || Dims[0] != 1 && Dims[1] != 1 ) { vc_error("DMAT only works with a 1xN or Nx1 matrix, this one is %dx%d", Dims[0], Dims[1]);
}
Size = vc_array_element_count(A);/* Set up dimensions of new array */
Dims[0] = Size;
Dims[1] = Size;
NewArray = vc_array_create(Dims, 2, TRUE);
Values = vc_array_values(A); /* Fetch the values for the diagonal */
for ( i = 0; i < Size; i++ ) { vc_array_set_values(NewArray, &Values[i], i*(Size+1), 1);
}
return(NewArray);
}