Extending the NCL function and procedure set

Writing your own wrapper

NCL has been designed to allow users the ability to extend the built-in default function set. This functionality requires a fair amount of C and Fortran knowledge. In addition, if you intend to call a Fortran function, some specific knowledge of how to call Fortran from C is needed. With release 4.1 extending NCL to call FORTRAN subroutines and functions has been greatly simplified. At release 4.1 the external statment was added to support loading of specially created shared libraries. Also a utility called wrapit77 was added to generate all the C code necessary to build an NCL shared library.

If you want to build your own custom version of NCL which includes your functions and procedures, the following steps should be followed. However, if you want to enable the installed version of NCL to call you functions and procedures using the external statement then follow the instructions for writing a wrapper, if you're calling a C function, and then follow the directions for building a shared library. If you're calling FORTRAN then follow all the steps for using wrapit77

The general approach for extending the NCL function set is as follows:

The NCL binaries installed on your system should contain a library called "libncl.a". To determine if this library has been installed on your system, run NCL and enter the following command: If libncl.a is not one of the libraries listed by the above command, contact your site administrator.

This section uses an example to demonstrate how to extend the function set. In this example we will write a function that evaluates a polynomial given a value and arrays of polynomial coefficients. The intended NCL function interface is:

function poly(
        a[*]:float,
        b[*]:float,
        c[*]:float,
        x[1]:float
)

Writing the function in C or Fortran

There are only a few restrictions necessary for C and Fortran functions. First and most important, the C function, Fortran function, or Fortran subroutine name must not conflict with any function names in the NCL library. Use the UNIX function "nm" to accomplish this. Second, for C, none of the data passed in from the wrapper can be freed. Finally, for Fortran, if I/O is being done you must make sure that the unit number being used is not already in use. It is also important to follow good programming practice by freeing any memory allocated by the function unless it is being passed back as a return value, and closing all files opened by the function. Also, only the internal functions specified on this page should be used.

The following is the C source for the function poly in a file called poly.c :

void poly_C(float *outval, float *a, float *b, float *c, float x, int n) {
        int i;

        for(i = 0; i < n ; i++) {
                outval[i] = (a[i] * x * x) + (b[i] * x) + c[i];
        }
}

Writing the wrapper function

The wrapper function must be written in C and use the function NclGetArgValue to retrieve the values of the parameters from NCL. The interface for the NclGetArgValue provides information as to the number of dimensions, dimension sizes, the missing values if any, and the type. The prototype for NclGetArgValues is located in "NclBuiltInSupport.h". The interface for NclGetArgValue follows:

The input argument arg_num is the number of the argument requested. The arguments are numbered from left to right (as they appear in NCL) starting at 0 through n-1 where n is the total number of arguments to the function. The input argument n_args is the total number of arguments for the NCL function. The final input parameter is access_type. Access_type tells NCL whether or not the parameter being accessed will be modified or not. This is necessary to support NCL's pass-by-value parameter passing scheme.

The actual data associated with the parameter is returned as a void pointer by the NclGetArgValue function which must be cast to the appropriate type. The parameters n_dims and dimsizes represent the rank of the parameter. The type parameter is set to one of the following basic data types. NclBasicDataTypes is defined in "NclDataDefs.h".

The contents of the has_missing parameter is set to 1 if the parameter possibly contains missing values. There are some situations where it is possible to have has_missing set and not actually have a missing value in the data. This occurs when a subsection of a variable, which has missing values, is subscripted and the portion subscripted does not actually contain missing values. However, if the has_missing parameter is set to 0, then there are no missing values. The value of the missing value is returned in the union NclScalar. NclScalar is defined in "NclDataDefs.h". It is a union of all the primitive types in NCL.

Finally, return values are passed back to the NCL environment via the NclReturnValue. NclReturnValue provides an interface for passing back data. It currently does not support passing back coordinate arrays or attributes, with the exception of missing values.

The prototype for the NclReturnValues function is:

One major goal of designing the wrapper function should be to establish a scheme for handling missing values if the function you are adding does not already handle them. In the following example, missing values are filtered out and propagated through to the output.

The source for the wrapper function follows:

Writing NclAddUserFuncs to register the function with NCL

Note:If you intend to create a shared library for use with either NCL_DEF_LIBRARY_DIR or the external statment follow these instructions but name the function Init rather than NclAddUserFuncs. Also if you intend to call a FORTRAN function or subroutine the utility wrapit77 can be used in place of this step in the wrapper creation.

The next step in extending the NCL function set is to write a function called NclAddUserFuncs. This function takes no arguments and does not return anything. Its purpose is to allow the user to call NclRegisterFunc and NclRegisterProc to tell NCL the NCL string name of the function, what the function pointer to call is, how many parameters the function takes, and what the type and dimensionality of the parameters are.

There are three C functions that must be called to register a C wrapper function as as an NCL built-in function. For each function being registered in NclAddUserFuncs, the function NewArgs must be called to initialize a private array that will hold the descriptions of each parameter. NewArgs takes a single integer argument that is the number of arguments for the function to be registered. It returns a void pointer that must be used in subsequent calls to SetArgTemplate and NclRegisterFunc.

The next function called is SetArgTemplate. It must be called for each parameter to the function. SetArgTemplate takes the pointer returned by NewArgs, the current parameter number, the parameter's type, number of dimensions, and dimension sizes as input. SetArgTemplate does not return a value; it simply modifies the private array returned by NewArgs.

Once each parameter has been defined by SetArgTemplate, then either the function NclRegisterFunc or NclRegisterProc can be called depending on whether the C function being added is meant to be an NCL function or procedure. The prototypes for these four functions are located in the include file NclBuiltIns.h, which should be located in ${NCARG_ROOT}/include/ncarg/ncl:

The following is the source for the function NclAddUserFuncs. More than one user function can be registered from within this function.

Compiling and linking files

Note: at the 4.1 release new functionality was added to support loading shared libraries to extend NCL. The directions for compiling a shared library can be found in the section titled Using wrapit77 to generate wrappers. This is the recommended method of extending NCL since a new version of NCL does not need to be created.

The final step is to compile and link the source files created with libncl.a and the other NCAR Graphics libraries. The simplest way to compile these functions into an executable is to use the nhlcc script from the UNIX prompt. nhlcc should be located in ${NCARG_ROOT}/bin. Nhlcc is a script that will link in the appropriate HLU, LLU, and other support libraries. The compile line for this example using the nhlcc command is:

AddUserFuncs.c contains the function NclAddUserFuncs and the function stubs NclAddUserFileFormats and NclAddUserHLUObjs, poly_w.c contains the wrapper function, and poly.c contains the actual function source. The extra libraries on the end are for netCDF, HDF, and lex respectively, which are used by NCL. The following is a list of all libraries needed in the order they should linked in to compile line:

Writing your own wrapper as a shared library

To create a wrapper and compile it as a shared library the steps of writing a C or Fortran function or procedure and writing a wrapper function are the same. However, instead of creating a C function called NclAddUserFuncs you must create a C function name "Init" which contains the same calls as NclAddUserFuncs. Then you must compile each piece of source code and link them together as a shared library.

Using wrapit77 to generate wrappers

With release 4.1 a utility application named wrapit77 is included with the release. This utility facilitates generating a C source code wrapper for Fortran 77 functions and subroutines. Below is a step by step description of how to use wrapit77. Starting with a Fortran procedure and ending with a functionally equivalent NCL procedure is a five-step process:
Step 1
Create code blocks that describe the Fortran procedure and its arguments. These code blocks are similar to Fortran 90 interface blocks (although the blocks created here must use Fortran 77 syntax), and C function prototypes.
Step 2
Create an NCL wrapper function by running the program wrapit77 that is supplied with the NCAR Graphics Software (version 4.1 or later). wrapit77 uses the code blocks in Step 1 as input.
Step 3
Create object modules (".o" files) for the NCL wrapper function generated in Step 2 and for the original Fortran source.
Step 4
Create a dynamic shared object from the object files created in Step 3 using the UNIX ld command. A dynamic shared object is an object module whose entries are loaded on an as-needed basis.
Step 5
Inform NCL where to locate the dynamic shared object created in Step 4.
Consider each of the above steps in detail:

Step 1 - describe your procedure

A wrapit interface block is a sequence of Fortran 77 statements that specifies a procedure and its arguments. These statements are bracketed by Fortran comment lines:
C NCLFORTSTART 
at the start and
C NCLEND 
at the end. These comment lines are known as wrapit interface block delimiters. Any blanks in the above two lines are ignored and the lines are case-insensitive.

The following is an example of a wrapit interface block for a Fortran function that finds the arc length of a polyline:

C NCLFORTSTART 
      FUNCTION ARCLN(NUMPNT, POINTX, POINTY)
      DIMENSION POINTX(NUMPNT), POINTY(NUMPNT)
C NCLEND
The statements between wrapit interface block delimiters should contain only a procedure statement and non-executable declarations describing its arguments. Any other statements (except comment statements) in a wrapit interface block will cause wrapit77 to exit with an error. Unless specifically typed, all variables are typed according to Fortran default typing.

Fortran 90 codes can use wrapit77 to generate an NCL wrapper function if you can provide a wrapit interface block for it, i.e. provided that you can specify the Fortran 90 procedure interface with Fortran 77 syntax. In some cases this will not be possible, for example if you are using pointers or structures as arguments, if the Fortran 90 procedure can have missing arguments, if the procedure is recursive, or if you are using keyword arguments. Many times, however, it is easy to convert Fortran 90 syntax to Fortran 77. For example, the Fortran 90 statements:

      CHARACTER (LEN=*)::NAME
      REAL,INTENT(IN)::NUMBER
could be specified as
      CHARACTER*(*) NAME
      REAL NUMBER
in the wrapit interface block.

Note: wrapit77 scans its input file for wrapit interface blocks, so if you can easily arrange your Fortran codes so that the wrapit interface blocks are embedded in them, it may be more convenient for you to use your original Fortran source file as input to wrapit77, rather than use a separate file containing only wrapit interface blocks. See example 2 in this section for details on this.

Step 2 - create an NCL wrapper function by running wrapit77

wrapit77 scans its input for wrapit interface blocks and produces an NCL wrapper function as output. Any source code in the input to wrapit77 that lies outside of a wrapit interface block is ignored, so the input to wrapit77 can simply be a sequence of wrapit interface blocks, or it can be complete Fortran source with embedded wrapit interface blocks.

For example, if your input wrapit interface block is in file wrapper_input, then executing

wrapit77 < wrapper_input >! wrapper_W.c
will create the NCL wrapper function wrapper_W.c. See the examples in this section for sample uses of wrapit77. You can name your NCL wrapper function anything you want, but the usual convention is to append the "_W" followed by ".c" (since the wrapper function is C code).

Step 3 - create object modules

Object modules (".o" files) need to be created for your original Fortran source and for the NCL wrapper function created in Step 2. The NCL wrapper function is C source and contains some special include files. You should use the nhlcc command to create the ".o" file for the NCL wrapper function, since it will correctly locate the include files. For example, if you have named your NCL wrapper function wrapper_W.c, then execute
nhlcc -c wrapper_W.c
to create wrapper_W.o. For consistency with the use of nhlcc, you may want to use the command nhlf77 to create the ".o" file for your Fortran source, but any way is fine.

Step 4 - create a dynamic shared object module

Dynamic shared objects are object modules whose entries are loaded on an as-needed basis. The UNIX command ld is used to create the dynamic shared objects (".so" files) used with NCL. The object modules (".o" files) you created in Step 3 will be used to create your dynamic shared object. For each of the supported systems (except for Crays, where wrapit77 is not currently supported), the requirements are different. Here is a summary of what you need to do on each system (where the NCL wrapper function object code is named fcode_W.o and the object for the original Fortran is named fcode.o) to create a dynamic shared object named fcode.so:

Step 5 - inform NCL where to locate your dynamic shared object

NCL gets its information about externally defined procedures from dynamic shared objects, as created in Step 4.

Once you have created your dynamic shared object containing the NCL wrapper function and the object for the original code, there are three ways to tell NCL where to look for the dynamic shared object:

  1. Use an NCL external statement.
  2. Use the system environment variable LD_LIBRARY_PATH.
  3. Use the NCL environment variable NCL_DEF_LIB_DIR.

Accessing dynamic shared objects using an external statement

In this case, you insert a statement of the form:
external SO_NAME "path_name"
at the beginning of your NCL script. In this statement SO_NAME indicates the name you want the dynamic shared object to be known by in the NCL program and path_name is a relative or full pathname of the dynamic shared object file you created. The final argument to the external statement is an NCL string variable, so the quotes are required. The case where path_name is simply the name of the dynamic shared object itself (and not a pathname) is special and is treated in the "Accessing dynamic shared objects using LD_LIBRARY_PATH" section.

Once the dynamic shared object has been accessed and named, you can call your new NCL function or procedure with an NCL statement of the form:

SO_NAME::fortran_name(arguments)
where SO_NAME is the name that you gave the dynamic shared object in your NCL external statement and fortran_name is the name of the Fortran function or subroutine that you specified in your wrapit interface block. fortran_name must be entered as either all uppercase, or all lowercase, regardless of what case the original Fortran procedure name was. Using an NCL name that is all uppercase or lowercase will produce the same results. For example, if your original Fortran procedure was named "CalcX", then you can use either "CALCX" or "calcx" to invoke that function from NCL, but you cannot use "CalcX" or any other mixed-case form of the name. Most NCL programmers tend to use lowercase for procedure names. See the examples in this section for complete examples of using dynamic shared objects and calling your new NCL procedures.

Accessing dynamic shared objects using LD_LIBRARY_PATH

In this case, you insert a statement of the form:
external SO_NAME "shared_object_name"
at the beginning of your NCL script. In this statement, shared_object_name is the name of your dynamic shared object file. NCL will look in the directories listed in the environment variable LD_LIBRARY_PATH for the specified dynamic shared object. If it does not find it there, an error will be reported. LD_LIBRARY_PATH is a system environment variable and is a colon-separated list of directory pathnames. In attempting to set this environment variable, make sure that it is not already set for use by other applications; if it is, just add the pathname of the dynamic shared object to it.

Once NCL has located the dynamic shared object, you can invoke the function or procedure in the same way that was discussed above under "Accessing dynamic shared objects using an external statement."

Accessing dynamic shared objects using NCL_DEF_LIB_DIR

If the NCL environment variable NCL_DEF_LIB_DIR is defined and is a valid directory pathname, NCL will look in that directory first for dynamic shared objects (you can have as many dynamic shared objects in this directory as you want). Any entry in any dynamic shared object in the directory specified by NCL_DEF_LIB_DIR will be loaded automatically as an NCL built-in. In particular this means that, in order to invoke your new function or procedure, you should not prepend SO_NAME:: to the function or procedure name as was required in either of the two other methods for accessing dynamic shared objects discussed above. NCL will try to load everything in the directory specified by NCL_DEF_LIB_DIR, so you will get warning messages about files in the directory that are not shared objects.

This approach can be more convenient than the other two approaches, but it lacks the advantage of having the SO_NAME:: prepended to externally defined procedures which makes their externally defined status quickly recognized from the syntax.

The environment variables NCL_DEF_LIB_DIR and LD_LIBRARY_PATH are independent. The dynamic shared objects in the directory specified by NCL_DEF_LIB_DIR are loaded before any dynamic shared object specified on an external statement, whether that dynamic shared object is specified by way of a relative or direct pathname or by way of a directory specified using the LD_LIBRARY_PATH environment variable.

Examples

This section contains examples illustrating how to incorporate sample Fortran codes into NCL. Example 1 is of particular importance, since it illustrates in detail all of the steps required to incorporate Fortran procedures into NCL.

Example 1 -- Fortran subroutine and function

Begin with the Fortran source (in file ex01.f):
      SUBROUTINE CQUAD (A, B, C, NQ, X, QUAD)
      REAL X(NQ),QUAD(NQ)
C
C  Calculate quadratic polynomial values.
C
      DO 10 I=1,NQ
        QUAD(I) = A*X(I)**2 + B*X(I) + C
   10 CONTINUE
C
      RETURN
      END
      FUNCTION ARCLN (NUMPNT, POINTX, POINTY)
      DIMENSION POINTX(NUMPNT),POINTY(NUMPNT)
C
C  Calculate arc lengths.
C
      IF (NUMPNT .LT. 2) THEN
        PRINT *, 'ARCLN: Number of points must be at least 2'
        STOP
      ENDIF
      ARCLN = 0.
      DO 10 I=2,NUMPNT
        PDIST = SQRT((POINTX(I)-POINTX(I-1))**2 + 
     +                        (POINTY(I)-POINTY(I-1))**2)
        ARCLN = ARCLN + PDIST
   10 CONTINUE
C
      RETURN
      END
This first example follows in detail the five-step process described above.

Step 1 - define the wrapit interface block.

Create a file ex01.wib that contains the following two wrapit interface blocks:

C NCLFORTSTART
      SUBROUTINE CQUAD (A,B,C,NQ,X,QUAD)
      REAL X(NQ),QUAD(NQ)
C NCLEND
 
C NCLFORTSTART
      FUNCTION ARCLN (NUMPNT,POINTX,POINTY)
      DIMENSION POINTX(NUMPNT),POINTY(NUMPNT)
C NCLEND
Step 2 - create an NCL wrapper function.

Execute the following:

wrapit77 < ex01.wib >! ex01_W.c
to create the NCL wrapper function ex01_W.c.

Step 3 - create the object modules (the ".o" files).

Execute the following:

nhlcc -c ex01_W.c
nhlf77 -c ex01.f
to create ex01_W.o and ex01.o (on an HP system you will need to add a "+z" flag to each of the above commands).

Step 4 - create a dynamic shared object module.

For this, use the appropriate ld command listed in the section above discussing this. For example, if you are running on a Sun Solaris machine, then you would execute:

  ld -o ex01.so ex01_W.o ex01.o -G
to create the dynamic shared object ex01.so.

Step 5 - tell NCL where your dynamic shared object is.

The external statement in the following example script tells NCL where to look for the dynamic shared object you just created.

external EX01 "./ex01.so"
 
begin
;
; calculate three values of a quadratic equation
;
   nump = 3
   x = (/ -1., 0.0, 1.0 /)
   qval = new(nump,float)              
   EX01::cquad(-1, 2, 3, nump, x, qval) ; call the new NCL version of
                                        ; your original Fortran subroutine
   print("Polynomial value = " + qval)
 
;
; calculate an arc length.
;
   xc = (/ 0., 1., 2. /)
   yc = (/ 0., 1., 0. /)
   arclen = EX01::arcln(nump,xc,yc)     ; call the new NCL version of
                                        ; your original Fortran function
   print("Arc length = " + arclen)
 
end
If you submit the above script to the NCL interpreter, it produces the output:
opening: ./ex01.so
(0)     Polynomial value = 0
(1)     Polynomial value = 3
(2)     Polynomial value = 4
(0)     Arc length = 2.82843
The numbers in parentheses at the left in the above printout are an artifact of how the NCL print function works and are of no relevance in this example.

Example 2 -- embedded wrapit interface blocks

Instead of starting with the Fortran source as in example 1 above, you could have used the following code (in file ex02.f) as input to wrapit77:
C NCLFORTSTART
      SUBROUTINE CQUAD (A,B,C,NQ,X,QUAD)
      REAL X(NQ),QUAD(NQ)
C NCLEND
C
C  Calculate quadratic polynomial values.
C
      DO 10 I=1,NQ
        QUAD(I) = A*X(I)**2 + B*X(I) + C
   10 CONTINUE
C
      RETURN
      END

C NCLFORTSTART
      FUNCTION ARCLN (NUMPNT, POINTX, POINTY)
      DIMENSION POINTX(NUMPNT),POINTY(NUMPNT)
C NCLEND
C
C  Calculate arc lengths.
C
      IF (NUMPNT .LT. 2) THEN
        PRINT *, 'ARCLN: Number of points must be at least 2'
        STOP
      ENDIF
      ARCLN = 0.
      DO 10 I=2,NUMPNT
        PDIST = SQRT((POINTX(I)-POINTX(I-1))**2 +
     +                        (POINTY(I)-POINTY(I-1))**2)
        ARCLN = ARCLN + PDIST
   10 CONTINUE
C
      RETURN
      END
In this example, the wrapit interface blocks are embedded directly into the Fortran code, thus avoiding the need to create a separate file containing them. All that wrapit77 is looking for in its input is blocks delimited by comment lines containing NCLFORTSTART and NCLEND.

Now execute:

wrapit77 < ex02.f >! ex02_W.c
and proceed as in steps 3-5 of Example 1.

Example 3 -- subroutine from a commercial library

Suppose you are calling:
      SUBROUTINE LIBSUB(ARG1)
from a commercial library named commercial.a. Using the following wrapit interface block (in file libsub.wib):
C NCLFORTSTART
      SUBROUTINE LIBSUB(ARG1)
C NCLEND
Create an NCL wrapper function and its object module by executing:
wrapit77 < libsub.wib > libsub_W.c
nhlcc -c libsub_W.c
To create a dynamic shared object named libsub.so, use the appropriate ld command by loading libsub_W.o and commercial.a. For example, if you are running on a Sun Solaris machine, you would execute:
  ld -o libsub.so libsub_W.o commercial.a -G
to create the dynamic shared object libsub.so.

Example 4 -- subroutine with CHARACTER input and output arguments

Start with a Fortran subroutine that takes an input string and returns a number of letters based on the length of the input string:
      SUBROUTINE EX04 (STRIN,STROUT)
      CHARACTER*(*) STRIN
      CHARACTER*26  ABET,STROUT
      DATA ABET/'ABCDEFGHIJKLMNOPQRSTUVWXYZ'/
C
      IMX = MIN(LEN(STRIN),26)
      STROUT = ABET(1:IMX)
C
      RETURN
      END
You could use embedded wrapit interface blocks, as in example 2 above, or use the following wrapit interface block:
C NCLFORTSTART
      SUBROUTINE EX04 (STRIN,STROUT)
      CHARACTER*(*) STRIN
      CHARACTER*26  STROUT
C NCLEND
to create the NCL wrapper and dynamic shared object (named ex04.so). Passing the following NCL script to the NCL interpreter:
external EXAMPLE04_SO "./ex04.so"
 
begin
 
 cstr = new(26,character)        ;  create a character array of length 26
 EXAMPLE04_SO::ex04("fifteen letters",cstr)
 str = chartostring(cstr)
 print(str)
 
end
produces the output:
opening: ./ex04.so
 
 
Variable: str
Type: string
Total Size: 4 bytes
            1 values
Number of Dimensions: 1
Dimensions and sizes:   [1]
Coordinates: 
(0)     ABCDEFGHIJKLMNO

Example 5 -- subroutine with a 2-dimensional array; printing

Start with one subroutine that calculates a function of two variables and stores the results in a 2-dimensional array, and another subroutine that prints 2-dimensional arrays by rows.
      SUBROUTINE EX05(M,N,X,Y,FXY)
      REAL X(M),Y(N),FXY(M,N)
C
C  Calculate FXY(I,J) = 2*I+J
C
      DO 10 J=1,N
        DO 20 I=1,M
          FXY(I,J) = 2.*REAL(I) + REAL(J)
   20   CONTINUE
   10 CONTINUE
C
      RETURN
      END
      SUBROUTINE PRT2D(M,N,A)
      REAL A(M,N)
C
C  Print the array A by rows using an F6.1 format with
C  7 values per line.
C
      DO 10 J=1,N
        PRINT *,'Row',J,':'
        DO 20 I=1,M/7
          WRITE(6,500) (A(LL,J),LL=(I-1)*7+1,I*7)
  500     FORMAT(7F6.1)
   20   CONTINUE
        IF (MOD(M,7) .NE. 0) WRITE(6,500) (A(LL,J),LL=(M/7)*7+1,M)
        PRINT *,' '
   10 CONTINUE
C
      RETURN
      END
Use the following wrapit interface block:
C NCLFORTSTART
      SUBROUTINE EX05(M,N,X,Y,FXY)
      REAL X(M),Y(N),FXY(M,N)
C NCLEND
C NCLFORTSTART
      SUBROUTINE PRT2D(M,N,A)
      REAL A(M,N)
C NCLEND
to create the NCL wrapper function and the dynamic shared object (named ex05.so). Then the following NCL script:
external EX05 "./ex05.so"
 
begin
;
; calculate three values of a quadratic equation
;
   m = 11
   n = 3
   x = new(m,float)
   y = new(n,float)
   fxy = new((/n,m/),float)
   EX05::ex05(m,n,x,y,fxy)
   EX05::prt2d(m,n,fxy)
end
will create the 2-dimensional array fxy in a manner compatible with other NCL procedures. Passing the above NCL script to the NCL interpreter produces the output:
opening: ./ex05.so
 Row  1:
     3.0   5.0   7.0   9.0  11.0  13.0  15.0
    17.0  19.0  21.0  23.0
  
 Row  2:
     4.0   6.0   8.0  10.0  12.0  14.0  16.0
    18.0  20.0  22.0  24.0
  
 Row  3:
     5.0   7.0   9.0  11.0  13.0  15.0  17.0
    19.0  21.0  23.0  25.0

Special considerations

This section contains several things that you should know to avoid common problems.
Array dimensioning:
For NCL arrays, the fastest-varying dimension is the rightmost, while for Fortran it is the leftmost dimension. Therefore, if XA is a Fortran array dimensioned idim x jdim, this array will be dimensioned jdim x idim in NCL. Also, Fortran array subscripts start at 1, whereas NCL array subscripts start at 0. Example 5 in this section illustrates these concepts.

Arrays of character strings:
Currently wrapit77 honors only non-dimensioned Fortran type CHARACTER variables. You cannot pass arrays of NCL strings to Fortran, nor can you pass Fortran CHARACTER arrays from Fortran back to NCL.

Passing strings from NCL to Fortran:
If you want to pass an NCL variable of type string to a Fortran procedure, then the argument to the Fortran procedure must be declared as CHARACTER*(*). See example 4 in this section.

Passing Fortran CHARACTER variables to NCL:
If you want to pass a Fortran CHARACTER variable back to NCL, then the Fortran argument must be a variable of type CHARACTER of fixed length, and the corresponding NCL variable must be a character array of the same length. If you want to use the NCL character array as an NCL string, you will need to use the NCL conversion function chartostring. See example 4 in this section.

Complex numbers:
NCL does not have a complex data type. If you want to bring complex numbers into NCL, you will have to do it by bringing in the real and imaginary parts as separate arrays. This will most likely require that you write an interface subroutine to your Fortran code that splits up the Fortran COMPLEX numbers into real and imaginary parts. Although you will not be able to do arithmetic on the complex numbers in NCL, you can still do analysis on the real and imaginary parts separately.

Procedure name conflicts:
If the procedure that you are incorporating into NCL has the same name as a currently existing built-in NCL procedure, NCL will choose its built-in and not your procedure. However, most UNIX ld commands recognize the -B symbolic flag, and using it when you create your dynamic shared object will force NCL to load your procedure in preference to its own built-ins. The -B symbolic can cause ld to report missing entries that in fact are ultimately not missing. It is probably best just to be careful to avoid defining a procedure with the same name as an NCL built-in.

NCL termination:
If a Fortran procedure that you have incorporated into NCL executes a STOP statement, or if a C function executes an exit statement, then the NCL interpreter will abort.

Unsupported Fortran 77 syntax in wrapit interface blocks:
Fortran COMMON blocks:
Fortran COMMON blocks are not allowed in a wrapit interface block. This would preclude your having adjustable arrays whose dimensions are passed in a COMMON block, or using COMMON to pass values for variables.
Fortran ENTRY statements:
There is no way to accommodate an ENTRY statement in a Fortran procedure.
Alternate return arguments:
Subroutines with alternate return arguments are not allowed.

Reference Manual Control Panel

NG4.1 Home, Index, Examples, Glossary, Feedback, Ref Contents, Ref WhereAmI?