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:
ncl 0> system("ls " + ncargpath("lib"))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 )
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]; } }
void *NclGetArgValue( int arg_num, /* IN: argument number to retrieve */ int n_args, /* IN: total number of arguments */ int* n_dims, /* OUT: number of dimensions of parameter */ int* dimsizes, /* OUT: dimension sizes of parameter */ NclScalar* missing, /* OUT: missing value if any */ int *has_missing, /* OUT: whether or not a missing value is present in the parameter */ NclBasicDataTypes *type, /* OUT: data type of parameter */ int access_type /* IN: Either 0 for don't care, 2 for a read or 1 for a write*/ );
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".
typedef enum { NCL_none = 0, NCL_short = 01, NCL_int = 02, NCL_long = 04, NCL_float = 010, NCL_double = 020, NCL_char = 040, NCL_byte = 0100, NCL_string = 0200, NCL_numeric = 0400, NCL_logical = 01000, NCL_obj = 02000 } NclBasicDataTypes;
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.
typedef union _NclScalar { double doubleval; float floatval; int intval; long longval; short shortval; char charval; string stringval; byte byteval; logical logicalval; obj objval; }NclScalar;
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:
NhlErrorTypes NclReturnValue( void *value, /* Pointer to data to be returned */ int n_dims, /* Number of dimensions of the data */ int* dimsizes, /* Dimension sizes of the data */ NclScalar* missing, /* If non-NULL is the missing value for the data otherwise NULL if no missing values in data */ NclBasicDataTypes type, /* Ncl basic type identifier */ int copy_data /* True if data should be copied before returned */ );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:
#include <stdio.h> /* * The following are the required NCAR Graphics include files. * They should be located in ${NCARG_ROOT}/include */ #include <ncarg/hlu/hlu.h> #include <ncarg/hlu/NresDB.h> #include <ncarg/ncl/defs.h> #include <ncarg/ncl/NclDataDefs.h> #include <ncarg/ncl/NclBuiltInSupport.h> /* * Declare C function version */ extern void poly_C(float *outval, float *a, float *b, float *c, float x, int n); NhlErrorTypes poly_W( void ) { float *a; int n_dims_a,dimsizes_a[NCL_MAX_DIMENSIONS],has_missing_a; NclScalar missing_a; float *b; int n_dims_b,dimsizes_b[NCL_MAX_DIMENSIONS],has_missing_b; NclScalar missing_b; float *c; int n_dims_c,dimsizes_c[NCL_MAX_DIMENSIONS],has_missing_c; NclScalar missing_c; float *x; NclScalar missing_x; float *output_val; NclScalar tmp_missing; int has_missing_x; int i; /* * Retrieve parameters */ /* * Note any of the pointer parameters can be set to NULL, which * implies you don't care about the its value. In this example * the type parameter is set to NULL because the function * is later registered to only accept floating point numbers. * Also the parameter x is later registered to be a single * dimension floating point value so dimension sizes and the * number of dimensions are not needed. */ a = (float*)NclGetArgValue( 0, 4, &n_dims_a, dimsizes_a, &missing_a, &has_missing_a, NULL, 2 ); b = (float*)NclGetArgValue( 1, 4, &n_dims_b, dimsizes_b, &missing_b, &has_missing_b, NULL, 2 ); c = (float*)NclGetArgValue( 2, 4, &n_dims_c, dimsizes_c, &missing_c, &has_missing_c, NULL, 2 ); x = (float*)NclGetArgValue( 3, 4, NULL, NULL, &missing_x, &has_missing_x, NULL, 2 ); /* * This is the only dimension size check needed since the function * is registered to only accept single dimension parameters. */ if((dimsizes_a[0] == dimsizes_b[0])&&(dimsizes_b[0] == dimsizes_c[0])) { /* * The case where x is a missing value should be handled. One option * is to print an error message and return NhlFATAL. However a better * option is to return an array of size n_dims_a filled with missing * values. */ if((has_missing_x) && (missing_x.floatval == *x)) { output_val = (float*)malloc(sizeof(float)*dimsizes_a[0]); for(i = 0; i < n_dims_a; i++) { output_val[i] = *x; } return(NclReturnValue( (void*)output_val, n_dims_a, dimsizes_a, &missing_x, NCL_float, 0 )); } /* * The following section allocates the output memory and calls the * poly function */ output_val = (float*)malloc(sizeof(float)*dimsizes_a[0]); poly_C(output_val,a,b,c,*x,dimsizes_a[0]); /* * A scheme for handling missing values in the input. NCL's algebra * handles missing value by propagating the missing value of the left * most term in an expression to the result whenever any term contains * a missing value. */ if(has_missing_a || has_missing_b || has_missing_c) { /* * Chooses suitable missing value by checking from left to right for * missing values. */ if(has_missing_a) { tmp_missing.floatval == missing_a.floatval; } else if (has_missing_b) { tmp_missing.floatval == missing_b.floatval; } else { tmp_missing.floatval == missing_c.floatval; } /* * Checks for missing values in the input and changes output value to missing * value if any of the terms contain missing values */ for(i = 0; i < dimsizes_a[0]; i++) { if((has_missing_a)&&(a[i] == missing_a.floatval)) { output_val[i] = tmp_missing.floatval; } if((has_missing_b)&&(b[i] == missing_b.floatval)) { output_val[i] = tmp_missing.floatval; } if((has_missing_c)&&(c[i] == missing_c.floatval)) { output_val[i] = tmp_missing.floatval; } } /* * Returns value to NCL */ return(NclReturnValue( (void*)output_val, n_dims_a, dimsizes_a, &tmp_missing, NCL_float, 0 )); } else { /* * Returns value to NCL */ return(NclReturnValue( (void*)output_val, n_dims_a, dimsizes_a, NULL, NCL_float, 0 )); } } else { NhlPError(NhlFATAL,NhlEUNKNOWN,"poly: the dimension sizes of parameters a, b and c must be identical"); return(NhlFATAL); } }
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:
void *NewArgs( int n /* Total number of parameters to function being registered */ ); void SetArgTemplate( void* args, /* Array returned by NewArgs */ int arg_num, /* Argument number being defined */ char* type_name, /* string representing type, set to NclANY if any type is accepted */ int n_dims, /* number of dimensions of argument, set to NclANY if any number of dimensions supported */ int* dimsizes /* dimension size for each dimension, set to NclANY if all dimension sizes are supported */ ); extern void NclRegisterFunc( NclPubBuiltInProcWrapper thefuncptr, /* The C wrapper function */ void* args, /* The array returned by NewArgs and initialized by successive calls to SetArgTemplate */ char* fname, /* String name of function to be used by NCL */ int n_args /* Total number of arguments */ );
The following is the source for the function NclAddUserFuncs. More than one user function can be registered from within this function.
#include <stdio.h> #include <ncarg/hlu/hlu.h> #include <ncarg/hlu/NresDB.h> #include <ncarg/ncl/defs.h> #include <ncarg/ncl/NclBuiltIns.h> /* * Declare wrapper function */ extern NhlErrorTypes poly_W( #if NhlNeedProto void #endif ); void NclAddUserFuncs #if NhlNeedProto (void) #else () #endif { void *args; int dimsizes[NCL_MAX_DIMENSIONS]; int nargs = 0; /* * Create private argument array */ args = NewArgs(4); /* * Configure first three parameters identically as single dimension float * arrays of any size */ SetArgTemplate(args,nargs,"float",1,NclANY);nargs++; SetArgTemplate(args,nargs,"float",1,NclANY);nargs++; SetArgTemplate(args,nargs,"float",1,NclANY);nargs++; /* * Configure fourth parameter as single scalar floating point value */ dimsizes[0] = 1; SetArgTemplate(args,nargs,"float",1,dimsizes);nargs++; /* * Register wrapper function pointer and argument templates */ NclRegisterFunc(poly_W,args,"poly",nargs); return; } /* * NOTE: the following function stubs must be included with the * source for function NclAddUserFuncs. Future releases of this * documentation will describe their uses. For now though NCL * WILL NOT compile WITHOUT them. */ void NclAddUserFileFormats #if NhlNeedProto (void) #else () #endif { return; } void NclAddUserHLUObjs #if NhlNeedProto (void) #else () #endif { return; }
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:
nhlcc -o ncl ${NCARG_ROOT}/lib/libncl.a AddUserFuncs.c poly_w.c poly.c -L${NCARG_ROOT}/lib -lnetcdf -ldf -llAddUserFuncs.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:
libhlu.a HLU library libncarg.a NCAR Graphics LLU library libncarg_gks.a NCAR Graphics GKS library libncarg_c.a NCAR Graphics common code libl.a System LEX library libnetcdf.a NetCDF library libX11.a X11 library libdf.a HDF library libF77.a NOTE: This library is part of the C-Fortran interface libraries it might be different for different architectures consult the Fortran programmers manual for the machine you are using. libI77.a NOTE: This library is part of the C-Fortran interface libraries it might be different for different architectures consult the Fortran programmers manual for the machine you are using. libU77.a NOTE: This library is part of the C-Fortran interface libraries it might be different for different architectures consult the Fortran programmers manual for the machine you are using. libm.a System Math library libc.a C library
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.
C NCLFORTSTARTat the start and
C NCLENDat 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 NCLENDThe 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)::NUMBERcould be specified as
CHARACTER*(*) NAME REAL NUMBERin 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.
For example, if your input wrapit interface block is in file wrapper_input, then executing
wrapit77 < wrapper_input >! wrapper_W.cwill 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).
nhlcc -c wrapper_W.cto 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.
nhlcc -c fcode_W.c nhlf77 -c fcode.f ld -shared -o fcode.so fcode_W.o fcode.o
nhlcc +z -c fcode_W.c nhlf77 +z -c fcode.f ld -b -o fcode.so fcode_W.o fcode.o
nhlcc -c fcode_W.c nhlf77 -c fcode.f ld -shared -o fcode.so fcode_W.o fcode.o
nhlcc -c fcode_W.c nhlf77 -c fcode.f ld -64 -shared -o fcode.so fcode_W.o fcode.o
nhlcc -c fcode_W.c nhlf77 -c fcode.f ld -n32 -shared -o fcode.so fcode_W.o fcode.o
nhlcc -c fcode_W.c nhlf77 -c fcode.f ld -o fcode.so fcode_W.o fcode.o -G
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:
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.
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."
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.
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 ENDThis 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 NCLENDStep 2 - create an NCL wrapper function.
Execute the following:
wrapit77 < ex01.wib >! ex01_W.cto 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.fto 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 -Gto 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) endIf 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.82843The 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.
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 ENDIn 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.cand proceed as in steps 3-5 of Example 1.
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 NCLENDCreate an NCL wrapper function and its object module by executing:
wrapit77 < libsub.wib > libsub_W.c nhlcc -c libsub_W.cTo 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 -Gto create the dynamic shared object libsub.so.
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 ENDYou 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 NCLENDto 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) endproduces 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
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 ENDUse 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 NCLENDto 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) endwill 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
NG4.1 Home, Index, Examples, Glossary, Feedback, Ref Contents, Ref WhereAmI?