Chapter 11: WRITING EXTERNAL FUNCTIONS

Ch11 Sec1. OVERVIEW

External functions are user-written Fortran routines which are called from the Ferret command line just as internal Ferret functions (e.g. SIN, COS) are invoked. For example, you might create a routine to compute the amplitudes of the Fourier transform of a time series (the periodogram) and name your function "FFT_AMP". In Ferret you would use it like this:

LET my_fft = FFT_AMP(my_time_series)

Once the variable my_fft is defined, it can be used in other expressions, plotted, etc. External functions can be used in every way that Ferret internal functions are used and are distinguished only by their appearance after internal functions when the user issues a SHOW FUNC command.

A Ferret external function uses input arguments defined in a Ferret session and computes a result with user-supplied Fortran code. The external function specifies how the grid for the result will be generated. The axes can be inherited from the input arguments or specified in the external function code.

Utility functions, linked in when the external function is compiled, obtain information from Ferret about variables and grids. The utility functions are described in section 6 (p. 229).

Ferret external functions are compiled individually to create shared object (.so) files. These are dynamically linked with Ferret at run time. Ferret looks for shared object files in the directories specified in the FER_EXTERNAL_FUNCTIONS environment variable.


Ch11 Sec2. GETTING STARTED

Ferret Version 5.0 and later contains everything you need to run the external functions which are included with the distribution. The environment variable FER_EXTERNAL_FUNCTIONS is defined, listing the directory where the shared object files reside. To see a list of the included external functions and their arguments, type

> ferret

yes? SHOW FUNC/EXTERNAL
Externally defined functions available to Ferret:
ADD_9(A,B,C,D,E,F,G,H,I)
   (demonstration function) adds 9 arguments
AVET(A)
   (demonstration function) returns the time average
   A: data to be time averaged
...



Ch11 Sec2.1. Getting example/development code

To write your own external functions, you will need to get source code and set up a directory in which to work. All of the source code you need to get started (Makefiles, common files, simple examples) can be obtained from the Ferret Home Page. Go to the External Functions page and follow the instructions there.

You will need to download a tar file to get started. When you untar this file you will find that the ef_utility/ directory contains the ferret_cmn subdirectory, containing common files that you need to compile external functions.  The ef_utility/ directory must be in place before you can compile any of the other external function code. The examples/ directory contains source code and a Makefile. You will create further directories with your external functions source code and Makefiles patterned on what is in the exapmles directory.


Ch11 Sec3. QUICK START EXAMPLE

It's always easier to start coding from an example. Any of the external functions we provide should be documented well enough to serve as a starting point for writing a new function. In this section, we take the most trivial example function, pass_thru, and alter it to do something a little more interesting, if no more useful.


Ch11 Sec3.1. The times2bad20 function

We'll use the pass_thru(...) function as a template, modifying it into a times2bad20(...) function. This new function will multiply all values by 2.0 and will replace missing value flags with the value 20.0.

Inside any of the example functions, the areas that you need to (are allowed to) modify are set off with

c***********************************************************************
c*                                           USER CONFIGURABLE PORTION |
c*                                                                     |
c*                                                                     V

       —>Insert your code here<—

c*                                                                     ^
c*                                                                     |
c*                                           USER CONFIGURABLE PORTION |
c***********************************************************************

Here's what you need to do to create the new function:

1.    move to the examples/ directory

2.    copy pass_thru.F to times2bad20.F

3.    use your favorite editor to change each "pass_thru" to "times2bad20"

4.    go down into the "times2bad20_init" section and change the description of the function

5.    go to the "times2bad20_compute" subroutine and change the code to look like this


   c*           result(i,j,k,l) = bad_flag_result
             result(i,j,k,l) = 20

          ELSE

  c*           result(i,j,k,l) = arg1(i,j,k,l)
             result(i,j,k,l) = 2 * arg1(i,j,k,l)

Assuming you have downloaded all of the ef_utility/ directory development code and you are still in the examples/ directory, you should be able to (Figure 11_1)

figure11_01

  > make times2bad20.so
  > setenv FER_EXTERNAL_FUNCTIONS .
  > ferret
    …
  yes? use coads_climatology
  yes? let a = times2bad20(sst)
  yes? shade a[l=1]

Congratulations! You have just written your first external function.


Ch11 Sec4. ANATOMY OF AN EXTERNAL FUNCTION

Every Ferret external function contains an ~_init subroutine which describes the external function's arguments and result grid and a ~_compute subroutine which actually performs the calculation. Three other optional subroutines are available for requesting memory allocation; creating axis limits for the result variable which are extended with respect to the defined region (useful for derivative calculations, etc.); and creating custom axes for the result.

For the following discussion we will assume that our external function is called efname (with source code in a file named efname.F). Examples are also taken from the external functions examples/ directory which you installed when you downloaded the external functions code. This section will briefly describe the work done by the ~_init and ~_compute subroutines. The individual utility functions called by these subroutines are described in the section on Utility Functions below.

When you name your external functions, be aware that Ferret will search its internal function names before the external function names.  So if you use a name that is already in use, your function will not be called.  Use SHOW FUNCTION from Ferret to list the names that already are in use


Ch11 Sec4.1. The ~_init subroutine (required)

subroutine efname_init (id)

This subroutine specifies basic information about the external function. This information is used when Ferret parses the command line and checks the number of arguments; when it generates the output of SHOW FUNCTION/EXTERNAL; and in determining the result grid.

The following code from examples/subtract.F shows a typical example of an ~_init subroutine. For an example with more arguments please look at examples/add_9.F. For an example where a result axis is reduced with respect to the equivalent input axis take a look at examples/percent_good_t.F.

      SUBROUTINE subtract_init(id)

     INCLUDE 'ferret_cmn/EF_Util.cmn'

     INTEGER id, arg

     CALL ef_version_test(ef_version)

* **********************************************************************
*                                            USER CONFIGURABLE PORTION |
*                                                                      |
*                                                                      V

     CALL ef_set_desc(id,'(demonstration function) returns: A - B' )

     CALL ef_set_num_args(id, 2)   ! Maximum allowed is 9
     CALL ef_set_axis_inheritance(id, IMPLIED_BY_ARGS,
    .     IMPLIED_BY_ARGS, IMPLIED_BY_ARGS, IMPLIED_BY_ARGS)
     CALL ef_set_piecemeal_ok(id, NO, NO, NO, NO)

     arg = 1
     CALL ef_set_arg_name(id, arg, 'A')
     CALL ef_set_axis_influence(id, arg, YES, YES, YES, YES)

     arg = 2
     CALL ef_set_arg_name(id, arg, 'B')
     CALL ef_set_axis_influence(id, arg, YES, YES, YES, YES)
*                                                                      ^
*                                                                      |
*                                            USER CONFIGURABLE PORTION |
* **********************************************************************

     RETURN
     END



Ch11 Sec4.2. The ~_compute subroutine (required)

subroutine efname_compute (id, arg_1, arg_2, ..., result, wkr_1, wrk_2, ...)

This subroutine does the actual calculation. Arguments to the external function and any requested working storage arrays are passed in. Dimension information for the subroutine arguments is obtained from Ferret common blocks in ferret_cmn/EF_mem_subsc.cmn. The mem1lox:mem1hix, etc. values are determined by Ferret and correspond to the region requested for the calculation.@Body Text = In the ~_compute subroutine you may call other subroutines which are not part of the efname_compute.F source file.

      SUBROUTINE subtract_compute(id, arg_1, arg_2, result)

     INCLUDE 'ferret_cmn/EF_Util.cmn'
     INCLUDE 'ferret_cmn/EF_mem_subsc.cmn'

     INTEGER id

     REAL bad_flag(EF_MAX_ARGS), bad_flag_result
     REAL arg_1(mem1lox:mem1hix, mem1loy:mem1hiy,
    .     mem1loz:mem1hiz, mem1lot:mem1hit)
     REAL arg_2(mem2lox:mem2hix, mem2loy:mem2hiy,
    .     mem2loz:mem2hiz, mem2lot:mem2hit)
     REAL result(memreslox:memreshix, memresloy:memreshiy,
    .     memresloz:memreshiz, memreslot:memreshit)

* After initialization, the 'res_' arrays contain indexing information
* for the result axes.  The 'arg_' arrays will contain the indexing
* information for each variable's axes.

     INTEGER res_lo_ss(4), res_hi_ss(4), res_incr(4)
     INTEGER arg_lo_ss(4,EF_MAX_ARGS), arg_hi_ss(4,EF_MAX_ARGS),
    .     arg_incr(4,EF_MAX_ARGS)


* **********************************************************************
*                                            USER CONFIGURABLE PORTION |
*                                                                      |
*                                                                      V

     INTEGER i,j,k,l
     INTEGER i1, j1, k1, l1
     INTEGER i2, j2, k2, l2

     CALL ef_get_res_subscripts(id, res_lo_ss, res_hi_ss, res_incr)
     CALL ef_get_arg_subscripts(id, arg_lo_ss, arg_hi_ss, arg_incr)
     CALL ef_get_bad_flags(id, bad_flag, bad_flag_result)

     …

*                                                                      ^
*                                                                      |
*                                            USER CONFIGURABLE PORTION |
* **********************************************************************
     RETURN
     END

Please see the "Loop Indices" section for the example calculation.4.3


Ch11 Sec4.3. The ~_work_size subroutine (optional)

This routine allows the external function author to request that Ferret allocate memory (working storage) for use by the external function. The memory allocated is passed to the external function when the ~compute subroutine is called. The working storage arrays are assumed to be REAL*4 arrays; adjust the size of the arrays for other data types.  See the sample code under ef_get_coordinates (p. 239)  for  an example of allocating a REAL*8 work array.  The working storage is deallocated after the ~compute subroutine returns.

When working storage is to be requested, a call to ef_set_num_work_arrays must be in the ~init subroutine:

SUBROUTINE efname_init (id)

CALL ef_set_num_work_arrays (id,2)

A maximum of 9 work arrays may be declared.  At the time the ~work_size subroutine is called by Ferret, any of the utility functions that retrieve information from Ferret may be used in the determination of the appropriate working storage size.

Here is an example of a ~work_size subroutine:

      SUBROUTINE efname_work_size(id)
     INCLUDE 'ferret_cmn/EF_Util.cmn'
     INCLUDE 'ferret_cmn/EF_mem_subsc.cmn'
     INTEGER id
*
* ef_set_work_array_lens(id, array #, X len, Y len, Z len, T len)
*
     INTEGER nx, ny, id
     INTEGER arg_lo_ss(4,1:EF_MAX_ARGS), arg_hi_ss(4,1:EF_MAX_ARGS),
    .     arg_incr(4,1:EF_MAX_ARGS)
     CALL ef_get_arg_subscripts(id, arg_lo_ss, arg_hi_ss, arg_incr)

     NX = 1 + (arg_hi_ss(X_AXIS,ARG1) - arg_lo_ss(X_AXIS,ARG1))
     NY = 1 + (arg_hi_ss(Y_AXIS,ARG1) - arg_lo_ss(Y_AXIS,ARG1))

     CALL ef_set_work_array_lens(id,1,NX,NY,1,1)
     CALL ef_set_work_array_lens(id,2,NX,NY,1,1)
*                                                                                                      
     RETURN

In the argument list of the ~compute subroutine, the work array(s) come after the result variable.  Declare the workspace arrays using index bounds wrk1lox:wrk2hix, … which were set by  the ef_set_work_array_lens call above.

    SUBOUTINE efname_compute (arg_1, result, workspace1, workspace2)


*   Dimension the work arrays
     REAL workspace1(wrk1lox:wrk1hix, wrk1loy:wrk1hiy,
    .               wrk1loz:wrk1hiz, wrk1lot:wrk1hit)
     REAL workspace2(wrk2lox:wrk2hix, wrk2loy:wrk2hiy,
    .               wrk2loz:wrk2hiz, wrk2lot:wrk2hit)



Ch11 Sec4.4. The ~_result_limits subroutine (optional)

The result limits routine sets the limits on ABSTRACT and CUSTOM axes created by the external function.

An example ~result_limits routine might look like this:

      SUBROUTINE my_result_limits(id)
     INCLUDE 'ferret_cmn/EF_Util.cmn'
     INTEGER id, arg, NF
*
     INTEGER arg_lo_ss(4,EF_MAX_ARGS), arg_hi_ss(4,EF_MAX_ARGS),
    .     arg_incr(4,EF_MAX_ARGS)
     INTEGER lo, hi

     CALL ef_get_arg_subscripts(id, arg_lo_ss, arg_hi_ss, arg_incr)

     arg = 1
     lo = 1
     hi = (arg_hi_ss(T_AXIS,arg) - arg_lo_ss(T_AXIS,arg) + 1)/ 2
     call ef_set_axis_limits(id, T_AXIS, lo, hi)

     RETURN
     END



Ch11 Sec4.5. The ~_custom_axes subroutine (optional)

The ~custom_axes subroutine allows the external function author to create new axes that will be attached the the result of the ~compute subroutine. An example of such a function might take time series data with a time axis and create, as a result, a Fourier transform with a frequency axis.

The difficulty with the ~custom_axes subroutine is that not all the Ferret internal information is available to the external function at the time Ferret calls this routine.5


Ch11 Sec5. NOTES AND SUGGESTIONS


Ch11 Sec5.1. Inheriting axes

When creating an external function, you can get Ferret to do a lot of conformability checking for you if you "inherit axes" properly. This means that Ferret can be responsible for making sure that the arguments you pass to the function are of the proper dimensionality to be combined together in basic operations such as addition, multiplication etc. For any given axis orientation, X, Y, Z, or, T, two arguments are said to be conformable on that axis if 1) they are either of the same length, or 2) at least one of the arguments has a size of 1 on the axis. ( The terminology "size of 1" may equivalently be thought of as a size of 0. In other words, the data is normal to this axis.) When Ferret encounters a problem it will send an error message rather than passing the data to your external function which might result in a crash.

To get Ferret to do this kind of checking you should inherit axes from as many appropriate arguments as possible. For instance, in subtract.F we have the following sections of code:

 subtract_init(...)


CALL ef_set_axis_inheritance(id, IMPLIED_BY_ARGS,
.     IMPLIED_BY_ARGS, IMPLIED_BY_ARGS, IMPLIED_BY_ARGS)
...

This means that the axes of the result, and the index range of the result on those axes, will be determined by arguments.


arg = 1
CALL ef_set_arg_name(id, arg, 'A')
CALL ef_set_axis_influence(id, arg, YES, YES, YES, YES)

arg = 2
CALL ef_set_arg_name(id, arg, 'B')
CALL ef_set_axis_influence(id, arg, YES, YES, YES, YES)
...

Here we specify that each result axis is dependent upon the axes from both arguments. When Ferret sees this, it knows the arguments must be conformable before it passes them to the external function.

The advantages of this approach are best understood by thinking about this example function "MY_ADD_FUNCTION," which performs a simple addition:

LET arg1 = X[x=0:1:.1]
LET arg2 = Y[Y=101:102:.05]
LET my_result = MY_ADD_FUNCTION(arg1, arg2)

The desired outcome is that "my_result" is a 2-dimensional field which inherits its X axis from arg1 and its Y axis from arg2.

If arguments and result are on the same grid, you should inherit all axes from all arguments. In general, you should inherit axes from as many arguments as possible.


Ch11 Sec5.2. Loop indices

Note: Array indices need not start at 1.

Because the data passed to an external function is often a subset of the full data set, array indices need not start at 1.

Note: Indices on separate arguments are not necessarily the same.

This might occur, for instance, with variables from different data sets.

Because of this, we need to ask Ferret what the appropriate index values are for the result axes and for each axis of each argument. We also need to know whether the increment for each axis of each argument is 0 or 1. An increment of 0 would be returned, for example, as the Y axis increment of an argument which which was only defined on the X axis. The data for this argument would be replicated along the Y axis when needed in a calculation.

The following section of code from subtract.F retrieves the index and increment information:


CALL ef_get_res_subscripts(id, res_lo_ss, res_hi_ss, res_incr)
CALL ef_get_arg_subscripts(id, arg_lo_ss, arg_hi_ss, arg_incr)
...

Once we have this information we must make sure that we don't mix and match indices. It's possible that you can write code which will work in the very simplest cases but will fail when you try something like:

yes? let a = my_func(sst[d=1],airt[d=2])
yes? plot a[l=@ave]

The solution is straightforward if not very pretty: Assign a separate index to each axis of each argument and index them all separately. The code in subtract.F shows how to do it with two arguments:

      …
     i1 = arg_lo_ss(X_AXIS,ARG1)
     i2 = arg_lo_ss(X_AXIS,ARG2)
     DO 400 i=res_lo_ss(X_AXIS), res_hi_ss(X_AXIS)

        j1 = arg_lo_ss(Y_AXIS,ARG1)
        j2 = arg_lo_ss(Y_AXIS,ARG2)
        DO 300 j=res_lo_ss(Y_AXIS), res_hi_ss(Y_AXIS)

           k1 = arg_lo_ss(Z_AXIS,ARG1)
           k2 = arg_lo_ss(Z_AXIS,ARG2)
           DO 200 k=res_lo_ss(Z_AXIS), res_hi_ss(Z_AXIS)

           l1 = arg_lo_ss(T_AXIS,ARG1)
           l2 = arg_lo_ss(T_AXIS,ARG2)
           DO 100 l=res_lo_ss(T_AXIS), res_hi_ss(T_AXIS)


                 IF ( arg_1(i1,j1,k1,l1) .EQ. bad_flag(1) .OR.
    .                 arg_2(i2,j2,k2,l2) .EQ. bad_flag(2) ) THEN

                    result(i,j,k,l) = bad_flag_result

                 ELSE

                    result(i,j,k,l) = arg_1(i1,j1,k1,l1) -
    .                    arg_2(i2,j2,k2,l2)

                 END IF


                 l1 = l1 + arg_incr(T_AXIS,ARG1)
                 l2 = l2 + arg_incr(T_AXIS,ARG2)
100           CONTINUE

              k1 = k1 + arg_incr(Z_AXIS,ARG1)
              k2 = k2 + arg_incr(Z_AXIS,ARG2)
200        CONTINUE

           j1 = j1 + arg_incr(Y_AXIS,ARG1)
           j2 = j2 + arg_incr(Y_AXIS,ARG2)
300     CONTINUE

        i1 = i1 + arg_incr(X_AXIS,ARG1)
        i2 = i2 + arg_incr(X_AXIS,ARG2)
400  CONTINUE
     ...



Ch11 Sec5.3. Reduced axes

For external functions we introduce the concept of "axis reduction." The result of an external function will have axes which are either RETAINED or REDUCED with respect to the argument axes from which they are inherited. By default, all result axes have their axis reduction flag set to RETAINED. Every result axis which has it axis inheritance flag set to IMPLIED_BY_ARGS will have the same extent (context) as the argument axis from which it inherits. Setting the axis reduction flag to REDUCED means that the result axis is reduced to a point by the external function.

The axis reduction flag only needs to be applied when the result is reduced to a point but SET REGION information should still be applied to the external function arguments. (e.g. a function returning a status flag) In such a case the result axes should be IMPLIED_BY_ARGS and REDUCED. (as opposed to NORMAL and RETAINED)

The percent_good_t.F function is a good example of where the axis reduction flag needs to be set. This function takes a 4D region of data and returns a time series of values representing the percentage of good data at each time point. Inside the percent_good_t_init subroutine we see that the X, Y and Z axes are reduced with respect to the incoming argument:

* **********************************************************************
*                                            USER CONFIGURABLE PORTION |
*                                                                      |
*                                                                      V
     CALL ef_set_desc(id,
    . '(demonstration function) returns % good data at each time' )
     CALL ef_set_num_args(id, 1)
     CALL ef_set_axis_inheritance(id, IMPLIED_BY_ARGS,
    .     IMPLIED_BY_ARGS, IMPLIED_BY_ARGS, IMPLIED_BY_ARGS)
     CALL ef_set_axis_reduction(id, REDUCED, REDUCED, REDUCED,
    .     RETAINED)
     CALL ef_set_piecemeal_ok(id, NO, NO, NO, NO)
     arg = 1
     CALL ef_set_arg_name(id, arg, 'A')
     CALL ef_set_arg_desc(id, arg, 'data to be checked')
     CALL ef_set_axis_influence(id, arg, YES, YES, YES, YES)
*                                                                      ^
*                                                                      |
*                                            USER CONFIGURABLE PORTION |
* **********************************************************************

This arrangement allows the user to specify an X/Y/Z region of interest and have this region information used when the argument is passed to the function. If we had specified X/Y/Z as NORMAL axes, Ferret would have understood this to mean that all region information for these three axes can be ignored when the percent_good_t function is called. This is not what we want.


Ch11 Sec5.4. String Arguments

Ferret can pass strings to external functions. This may be useful if you are writing external functions to write a new output format, for example, and wish to pass the output filename as an argument.

By default, all arguments are assumed to be of type FLOAT_ARG. In the ~init subroutine, the external function must tell Ferret which arguments are to be handled as strings:

arg = 1
CALL ef_set_arg_type(id, arg, STRING_ARG)
CALL ef_set_arg_name(id, arg, 'message')
CALL ef_set_arg_desc(id, arg, 'String to be written when executing.')
CALL ef_set_axis_influence(id, arg, YES, YES, YES, YES)

In the ~compute subroutine, a pointer to the string argument is passed in and dimensioned as any other argument. A text variable must be declared and a utility function is used to get the actual text string. As an example:

      SUBROUTINE string_args_compute(id, arg_1, arg_2, result)

     INCLUDE 'ferret_cmn/EF_Util.cmn'
     INCLUDE 'ferret_cmn/EF_mem_subsc.cmn'

     INTEGER id

     REAL bad_flag(1:EF_MAX_ARGS), bad_flag_result
     REAL arg_1(mem1lox:mem1hix, mem1loy:mem1hiy,
    .           mem1loz:mem1hiz, mem1lot:mem1hit)

     REAL arg_2(mem2lox:mem2hix, mem2loy:mem2hiy,
    .           mem2loz:mem2hiz, mem2lot:mem2hit)
     REAL result(memreslox:memreshix, memresloy:memreshiy,
    .            memresloz:memreshiz, memreslot:memreshit)

     INTEGER res_lo_ss(4), res_hi_ss(4), res_incr(4)
     INTEGER arg_lo_ss(4,1:EF_MAX_ARGS), arg_hi_ss(4,1:EF_MAX_ARGS),
    .     arg_incr(4,1:EF_MAX_ARGS)

     CHARACTER arg1_text*160

* **********************************************************************
*                                            USER CONFIGURABLE PORTION |
*                                                                      |
*                                                                      V
     INTEGER i,j,k,l
     INTEGER i1, j1, k1, l1

     CALL ef_get_arg_string(id, 1, arg1_text)

     WRITE(6,49) arg1_text
49   FORMAT ('The text for arg1 is : ''',a,'''')

     …



Ch11 Sec6. UTILITY FUNCTIONS

The lists below describe the utility functions built into Ferret which are available to the external function writer. These are used to set parameters associated with the external function and to retrieve information provided by Ferret. (Input variables, sending information to Ferret, are in plain type and output variables, getting information from Ferret, are in italic.)


Ch11 Sec6.1. EF_Util.cmn

External functions need to include the EF_Util.cmn file in each subroutine in order to use various pre-defined parameters. These parameters are defined in the table below:

Parameters defined in EF_Util.cmn

To make the code more readable:

X_AXIS (=1)

ARG1 (=1)

ARG5 (=5)

ARG9 (=9)

Y_AXIS (=2)

ARG2 (=3)

ARG6 (=6)

YES  (=2)

Z_AXIS (=3)

ARG3 (=3)

ARG7 (=7)

NO   (=0)

T_AXIS (=4)

ARG4 (=4)

ARG8 (=8)


Internal parameters for Ferret:

CUSTOM

result axis is defined by the external function

IMPLIED_BY_ARGS

result axis is inherited from one (or more) of the arguments

NORMAL

this axis does not exist in the result

ABSTRACT

result axis is an indexed axis [1:N]

RETAINED

result axis has same extent as argument axis

REDUCED

result axis is reduced to a point

Ch11 Sec6.2. Available utility functions

Setting Parameters

General Information

Getting Information

For all calculations

Text

Values

Other

ef_set_desc(id, desc)

Assign a text string description to the external function.

Input arguments:

1.    INTEGER id: external function's ID number

2.    CHARACTER*(*) desc: description of this function

ef_set_num_args(id, num)

Specify the number of arguments this function will accept.  The maximum number of arguments allowed is 9

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER num: number of arguments for this function

ef_set_axis_inheritance(id, Xsrc, Ysrc, Zsrc, Tsrc)

Specify where the result axes will come from. The acceptable values for each axis will be one of:

CUSTOM

result axis is defined by the external function

IMPLIED_BY_ARGS

result axis is inherited from one (or more) of the arguments

NORMAL

this axis does not exist in the result

ABSTRACT

result axis is an indexed axis [1:N]


Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER Xsrc: inheritance flag for the X axis

3.    INTEGER Ysrc: inheritance flag for the Y axis

4.    INTEGER Zsrc: inheritance flag for the Z axis

5.    INTEGER Tsrc: inheritance flag for the T axis

ef_set_piecemeal_ok(id, Xyn, Yyn, Zyn, Tyn)

Tell Ferret whether it is ok to break up calculations along a particular axis.  (Not implemented as of Ferret v5.22, EF version 1.3)

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER Xyn: yes/no flag for the X axis

3.    INTEGER Yyn: yes/no flag for the Y axis

4.    INTEGER Zyn: yes/no flag for the Z axis

5.    INTEGER Tyn: yes/no flag for the T axis

ef_set_arg_name(id, arg, name)

Assign a text string name to an argument.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER arg: argument number

3.    CHARACTER*(*) name: argument name

ef_set_arg_desc(id, arg, desc)

Assign a text string description to an argument.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER arg: argument number

3.    CHARACTER*(*) desc: argument description

ef_set_arg_unit(id, arg, unit)

Assign a text string to an argument's units.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER arg: argument number

3.    CHARACTER*(*) unit: unit description

ef_set_arg_type(id, arg, type)

Specify the type of an argument as either FLOAT_ARG or STRING_ARG. In the ~_compute subroutine, the ef_get_arg_string() function is used to obtain the desired text string.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER arg: argument number

3.    INTEGER type: either FLOAT_ARG or STRING_ARG

ef_set_axis_extend(id, arg, axis, lo_amt, hi_amt)

Tell Ferret to extend the range of data passed for an argument. This is useful for cases like smoothers where the result at a particular point depends upon a range of input values around that point.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER arg: argument number

3.    INTEGER axis: axis number

4.    INTEGER lo_amt: extension to the lo range (–1 means get one more point than in the result)

5.    INTEGER hi_amt: extension to the hi range (+1 means get one more point than in the result)

ef_set_axis_influence(id, arg, Xyn, Yyn, Zyn, Tyn)

Specify whether this argument's axes "influence" the result axes. A value of YES for a particular axis means that the result should have the same axis as this argument. If the result should have the same axis as several input arguments, then each argument should specify YES for the axis in question. Note that ef_set_axis_inheritance must have specified IMPLIED_BY_ARGS for this axis.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER arg: argument number

3.    INTEGER Xyn: influence flag for the X axis

4.    INTEGER Yyn: influence flag for the Y axis

5.    INTEGER Zyn: influence flag for the Z axis

6.    INTEGER Tyn: influence flag for the T axis

ef_set_axis_reduction(id, Xred, Yred, Zred, Tred)

Specify whether the result axes are RETAINED or REDUCED with respect to the argument axes from which they are inherited. Setting the axis reduction flag to REDUCED means that the result axis is reduced to a point by the external function. The axis reduction flag need only be set when the result is reduced to a point but SET REGION information should still be applied to the external function arguments.

Input arguments:

1. INTEGER id: external function's ID number

2. INTEGER Xred: reduction flag for the X axis

3. INTEGER Yred: reduction flag for the Y axis

4. INTEGER Zred: reduction flag for the Z axis

5. INTEGER Tred: reduction flag for the T axis

ef_set_axis_limits(id, axis, lo, hi)

Specify the lo and hi limits of an axis. (This is not needed for most functions and must appear in a separate subroutine named ~func_name~_result_limits(id)).

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER axis: axis number

3.    INTEGER lo: index value of the lo range of this axis

4.    INTEGER hi: index value of the hi range of this axis

ef_set_custom_axis(id, axis, lo, hi, delta, unit, modulo)

Create a custom axis. This is only used by functions which create a custom axis and must appear in a separate subroutine named ~func_name~_custom_axes(id).

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER axis: axis number

3.    INTEGER lo: index value of the lo range of this axis

4.    INTEGER hi: index value of the hi range of this axis

5.    INTEGER delta: increment for this axis

6.    CHARACTER*(*) unit: unit for this axis

7.    INTEGER modulo: flag for modulo axes (1 = modulo)

ef_set_num_work_arrays(id, nwork)

Set the number of work arrays to be allocated.  The maximum number of work arrays allowed is 9.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER nwork: number of storage arrays

ef_set_work_array_dims(id, iarray, xlo, ylo, zlo, tlo, xhi, yhi, zhi, thi)

Set the working array axis lengths.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER iarray: array number

3.    INTEGER xlo: index value of the lo range of x axis

4.    INTEGER ylo: index value of the lo range of y axis

5.    INTEGER zlo: index value of the lo range of z axis

6.    INTEGER tlo: index value of the lo range of t axis

7.    INTEGER xhi: index value of the hi range of x axis

8.    INTEGER yhi: index value of the hi range of y axis

9.    INTEGER zhi: index value of the hi range of z axis

10.    INTEGER thi: index value of the hi range of t axis

ef_get_res_subscripts(id, res_lo_ss, res_hi_ss, res_incr)

Return lo and hi indices and increments to be used in looping through the calculation of the result.

Input arguments:

1.    INTEGER id: external function's ID number


Output arguments:

1.    INTEGER res_lo_ss(4): the lo end indices for the X, Y, Z, T axes of the result

2.    INTEGER res_hi_ss(4): the hi end indices for the X, Y, Z, T axes of the result

3.    INTEGER res_incr(4): the increment to be applied to the X, Y, Z, T axes of the result

Sample code:

CALL ef_get_res_subscripts(id,
res_lo_ss, res_hi_ss, res_incr) ... DO 400 i=res_lo_ss(X_AXIS),
res_hi_ss(X_AXIS) DO 300 j=res_lo_ss(Y_AXIS), res_hi_ss(Y_AXIS)
... 300 CONTINUE 400 CONTINUE

ef_get_arg_info(id, iarg, arg_name, arg_title, arg_units)

Return strings describing argument: name, title, units.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER iarg: argument number


Output arguments:

1.    CHARACTER*24 arg_name: the name of the argument

2.    CHARACTER*128 arg_title: title associated with the argument

3.    CHARACTER*32 arg_units: the argument's units.

ef_get_arg_string(id, iarg, text)

Return the string associated with an argument of type STRING_ARG.   The maximum length for the string ist 160 characters.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER iarg: argument number


Output arguments:

1.    CHARACTER*(*) text: the actual text string for the argument

Sample code:

      …

     CHARACTER arg_text*160

* **********************************************************************
*                                            USER CONFIGURABLE PORTION |
*                                                                      |
*                                                                      V
     INTEGER i,j,k,l
     INTEGER i1, j1, k1, l1

     CALL ef_get_arg_string(id, 1, arg_text)
     WRITE(6,49) arg_text
49   FORMAT ('The text is : ''',a,'''')

     …
ef_get_axis_info(id, iarg, axname, ax_units, backward, modulo, regular)

Return strings describing argument: name, title, units.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER iarg: argument number


Output arguments:

1.    CHARACTER*16 ax_name(4): the name of the four axes

2.    CHARACTER*16 ax_units(4): units of the four axes

3.    LOGICAL backward(4): true if axis is backward axis

4.    LOGICAL modulo(4): true if axis is modulo axis

5.    LOGICAL regular(4): true if axis is regular axis

ef_get_axis_dates(id, iarg, taxis, numtimes, datebuf)

Returns the string date buffer associated with the time axis of an argument.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER iarg: argument number

3.    REAL*8 taxis(numtimes): time axis coordinate values

4.    INTEGER numtimes: number of time


Output arguments:

1.    CHARACTER*20 datebuf(numtimes): the string-date buffer for each time.

ef_get_arg_subscripts(id, arg_lo_ss, arg_hi_ss, arg_incr)

Return lo and hi indices and increments to be used in looping through the calculation of the result.

Input arguments:

1.    INTEGER id: external function's ID number


Output arguments:

1.    INTEGER arg_lo_ss(4,EF_MAX_ARGS): the lo end indices for the X, Y, Z, T axes of each argument

2.    INTEGER arg_hi_ss(4,EF_MAX_ARGS): the hi end indices for the X, Y, Z, T axes of each argument

3.    INTEGER arg_incr(4,EF_MAX_ARGS): the increment to be applied to the X, Y, Z, T axes of each argument

Sample code:

INTEGER i,j,k,l
INTEGER i1, j1, k1, l1
INTEGER i2, j2, k2, l2

CALL ef_get_res_subscripts(id, res_lo_ss, res_hi_ss, res_incr)
CALL ef_get_arg_subscripts(id, arg_lo_ss, arg_hi_ss, arg_incr)
CALL ef_get_bad_flags(id, bad_flag, bad_flag_result)

i1 = arg_lo_ss(X_AXIS,ARG1)
i2 = arg_lo_ss(X_AXIS,ARG2)

DO 400 i=res_lo_ss(X_AXIS), res_hi_ss(X_AXIS)
 …
 i1 = i1 + arg_incr(X_AXIS,ARG1)
 i2 = i2 + arg_incr(X_AXIS,ARG2)
400 CONTINUE

ef_get_arg_ss_extremes(id, arg, ss_min, ss_max)

Return the maximum and minim index values for all the arguments. These define the domain of the data.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER arg: argument  number


Output arguments:

1.    INTEGER ss_min(4,EF_MAX_ARGS): the minimum indices for the X, Y, Z, T axes of each argum

2.    INTEGER ss_max(4,EF_MAX_ARGS): the maximum indices for the X, Y, Z, T axes of each argum

Sample code:

CALL ef_get_arg_ss_extremes(id, arg, ss_min, ss_max)

ef_get_bad_flags(id, bad_flag, bad_flag_result)

Return the missing value flags for each argument and for the result.

Input arguments:

1.    INTEGER id: external function's ID number


Output arguments:

1.    REAL bad_flag(EF_MAX_ARGS): missing value flags for each argument

2.    REAL bad_flag_result: missing value flag for the result

Sample code:

CALL ef_get_res_subscripts(id, res_lo_ss, res_hi_ss, res_incr)
CALL ef_get_arg_subscripts(id, arg_lo_ss, arg_hi_ss, arg_incr)
CALL ef_get_bad_flags(id, bad_flag, bad_flag_result)



IF ( arg_1(i1,j1,k1,l1) .EQ. bad_flag(ARG1) ) THEN
 result(i,j,k,l) = bad_flag_result
ELSE
 ...

ef_get_coordinates(id, arg, axis, lo, hi, coords)

Return the "world coordinates" associated with a particular arg, axis and lo:hi range.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER arg: argument number

3.    INTEGER axis: axis number

4.    INTEGER lo: lo index of desired range

5.    INTEGER hi: hi index of desired range

Output arguments:

1.    REAL*8 coords(*): array of "world coordinate" values (NB_ these values are associated with index values lo:hi but are returned as coords(1:hi-lo).)

Sample code: in the work_size subroutine, define twice as many elements as coordinates so as to have storage for REAL*8 numbers

*

      SUBROUTINE myfcn_work_size(id)
     INCLUDE 'ferret_cmn/EF_Util.cmn'
     INCLUDE 'ferret_cmn/EF_mem_subsc.cmn'
     INTEGER id

*  Set the work arrays,  X/Y/Z/T dimensions

     INTEGER nxout, nx2
     INTEGER arg_lo_ss(4,1:EF_MAX_ARGS), arg_hi_ss(4,1:EF_MAX_ARGS),
         arg_incr(4,1:EF_MAX_ARGS)

     CALL ef_get_arg_subscripts(id, arg_lo_ss, arg_hi_ss, arg_incr)

     nxout = 1 + arg_hi_ss(X_AXIS,ARG4) - arg_lo_ss(X_AXIS,ARG4)
     nx2 = nxout* 2

* Define work array XAX

     CALL ef_set_work_array_dims (id, 1, 1, 1, 1, 1, nx2, 1, 1, 1)
     RETURN
     END

In the compute subroutine, dimension the REAL*8 coordinate array with half the wkr1hix dimension (wrk1lox:wrk1hix, etc are defined by the work_size subroutine)


      SUBROUTINE myfcn_compute(id, arg_1, arg_2, result,  xax)

     REAL arg_1(mem1lox:mem1hix, mem1loy:mem1hiy, mem1loz:mem1hiz,
    .     Mem1lot:mem1hit)
     REAL result(memreslox:memreshix, memresloy:memreshiy,
    .     memresloz:memreshiz, memreslot:memreshit)

     INTEGER res_lo_ss(4), res_hi_ss(4), res_incr(4)
     INTEGER arg_lo_ss(4,EF_MAX_ARGS), arg_hi_ss(4,EF_MAX_ARGS),
    .     Arg_incr(4,EF_MAX_ARGS)

C  Dimension the work array: X dimension was  defined twice as large
C  as the # coordinates, for double precision work array.

     REAL*8 xax(wrk1lox:wrk1hix/2, wrk1loy:wrk1hiy,
    .     wrk1loz:wrk1hiz, wrk1lot:wrk1hit)


     CALL ef_get_res_subscripts(id, res_lo_ss, res_hi_ss, res_incr)
     CALL ef_get_arg_subscripts(id, arg_lo_ss, arg_hi_ss, arg_incr)
     CALL ef_get_bad_flags(id, bad_flag, bad_flag_result)

     CALL ef_get_coordinates(id, ARG1, X_AXIS, arg_lo_ss(X_AXIS,

     .  ARG1), arg_hi_ss(X_AXIS, ARG1), xax )



    dummy = 1
    DO 30 i = arg_lo_ss(Y_AXIS, ARG1), arg_hi_ss(Y_AXIS, ARG1)
      cstr(i) = 1.0 / cos( xax(dummy) * (1.0/radian) )
      dummy = dummy + 1
30   CONTINUE

ef_get_box_size(id, arg, axis, lo, hi, size)

Return the box sizes (in "world coordinates") associated with a particular arg, axis and lo:hi range.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER arg: argument number

3.    INTEGER axis: axis number

4.    INTEGER lo: lo index of desired range

5.    INTEGER hi: hi index of desired range


Output arguments:

1.    REAL size(*): array of box size values (NB_ these values are associated with index values lo:hi but are returned as coords(1:hi-lo).)

Sample code:

REAL tk_y(wrk1lox:wrk1hix, wrk1loy:wrk1hiy/2,
    .     wrk1loz:wrk1hiz, wrk1lot:wrk1hit)
REAL tk_dx(wrk2lox:wrk2hix, wrk2loy:wrk2hiy,
    .     wrk2loz:wrk2hiz, wrk2lot:wrk2hit)

INTEGER dummy



CALL ef_get_res_subscripts(id, res_lo_ss, res_hi_ss, res_incr)
CALL ef_get_arg_subscripts(id, arg_lo_ss, arg_hi_ss, arg_incr)
CALL ef_get_bad_flags(id, bad_flag, bad_flag_result)
CALL ef_get_coordinates(id, ARG1, Y_AXIS, arg_lo_ss(Y_AXIS, ARG1),
.     arg_hi_ss(Y_AXIS, ARG1), tk_y )
CALL ef_get_box_size(id, ARG1, X_AXIS, arg_lo_ss(X_AXIS, ARG1),
.     arg_hi_ss(X_AXIS, ARG1), tk_dx )



dummy = 1
DO 20 i = arg_lo_ss(X_AXIS, ARG1), arg_hi_ss(X_AXIS, ARG1)
 dxt4r(i) = 1.0 / ( 4.0 * tk_dx(dummy) * radius/radian )
 dummy = dummy + 1
20 CONTINUE

ef_get_box_limits(id, arg, axis, lo, hi, lo_lims, hi_lims)

Return the box limits (in "world coordinates") associated with a particular arg, axis and lo:hi range.

Input arguments:

1.    INTEGER id: external function's ID number

2.    INTEGER arg: argument number

3.    INTEGER axis: axis number

4.    INTEGER lo: lo index of desired range

5.    INTEGER hi: hi index of desired range


Output arguments:

1.    REAL lo_lims(*): array of box lower limit values (NB_ these values are associated with index values lo:hi but are returned as coords(1:hi-lo).)

2.    REAL hi_lims(*): array of box upper limit values (NB_ these values are associated with index values lo:hi but are returned as coords(1:hi-lo).)

ef_get_one_val(id, arg, value)

Return the value of 1×1×1×1 variable.

Input arguments:

1. INTEGER id: external function's ID number

2. INTEGER arg: argument number


Output arguments:

1. REAL value : The value of the variable

ef_version_test (version)

Return the version number of the external functions code that is in place.

Output argument:

1. REAL version : The version number

ef_bail_out(id, text)

Bail out of an external function, returning to Ferret and issuing a message to the user.

Input arguments:

1. INTEGER id: external function's ID number

2. INTEGER text: text string to output.



ferret_ug@pmel.noaa.gov

Last modified: September 27, 2000