Going beyond the basics

home | toc | about doc | intro | examples | basics | beyond basics | hints and tips | appendix | glossary

Incorporating your own Fortran or C code

When to incorporate your own codes

On occasion you may want to incorporate the functionality of currently existing Fortran or C codes into NCL. These codes may be your own personal codes, or they may exist in a local or commercial library. Some examples are when:

Note:In what follows, whenever "NCL procedures" is used, it should be understood to mean "NCL procedures and functions."

NCL wrapper functions

If you start with an external Fortran or C procedure and want to call it from NCL, you need to provide code that serves as an interface between NCL and the external procedure. This interface code is called an NCL wrapper function.

An NCL wrapper function intercepts an NCL procedure call, checks the arguments in that call, does the appropriate type conversions, and then calls the external Fortran or C procedure. NCL wrapper functions play a central role in incorporating external procedures into NCL.

Incorporating Fortran functions and subroutines

There is a step-by-step process (see below) for incorporating your Fortran procedures into NCL. At the heart of this process is the program wrapit77 that is used to create an NCL wrapper function. The process standardizes the argument list for the NCL procedure that is functionally equivalent to your original Fortran procedure. The arguments to your Fortran procedure are converted to NCL arguments on a one-for-one basis in an obvious way. For example, if your Fortran procedure has one argument that is a 1-dimensional REAL array and another argument that specifies the size of that array, then an equivalent NCL procedure will have an argument that is a 1-dimensional "float" array and another "integer" argument specifying the size of that array.

There are many possibilities for designing NCL procedures that improve upon the equivalent Fortran procedure. To do this requires that you write a custom NCL wrapper function, instead of using wrapit77. For example, if a Fortran subroutine takes an adjustable array and its dimension as arguments, then the dimension argument can be removed from the NCL procedure, since NCL can determine that information. How to write custom NCL wrappers is described in the "Extending the NCL function and procedure set" section of the NCL Reference Manual. Writing custom NCL wrappers for Fortran codes will ultimately require your calling the existing Fortran code from the custom NCL wrapper. Writing custom wrappers is required for C functions.

The process

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:
at the start and
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:

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:

could be specified as
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 path name 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 path name) 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:

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 path name 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 path name or by way of a directory specified using the LD_LIBRARY_PATH environment variable.

Incorporating C codes

There is no program that will generate wrapper functions for C codes as an equivalent to wrapit77 for Fortran codes. This is because the syntax for C functions is more complex than Fortran (recursive functions, pointers, structures, and so forth) to the point that writing an NCL wrapper generator program would not be cost effective. Also, since the NCL wrapper functions are written in C, it is easier to write custom NCL wrappers for C functions than for Fortran. To create custom NCL wrappers for C functions, see the "Extending the NCL function and procedure set" section of the NCL Reference Manual. C programmers should find this straightforward, but somewhat tedious.

After you have generated a custom NCL wrapper for your C code, you should proceed as described in Steps 3-5 as presented for Fortran codes above.


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):
C  Calculate quadratic polynomial values.
      DO 10 I=1,NQ
        QUAD(I) = A*X(I)**2 + B*X(I) + C
C  Calculate arc lengths.
      IF (NUMPNT .LT. 2) THEN
        PRINT *, 'ARCLN: Number of points must be at least 2'
      ARCLN = 0.
      DO 10 I=2,NUMPNT
        PDIST = SQRT((POINTX(I)-POINTX(I-1))**2 + 
     +                        (POINTY(I)-POINTY(I-1))**2)
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:

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"
; 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)
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  Calculate quadratic polynomial values.
      DO 10 I=1,NQ
        QUAD(I) = A*X(I)**2 + B*X(I) + C

C  Calculate arc lengths.
      IF (NUMPNT .LT. 2) THEN
        PRINT *, 'ARCLN: Number of points must be at least 2'
      ARCLN = 0.
      DO 10 I=2,NUMPNT
        PDIST = SQRT((POINTX(I)-POINTX(I-1))**2 +
     +                        (POINTY(I)-POINTY(I-1))**2)
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:
from a commercial library named commercial.a. Using the following wrapit interface block (in file libsub.wib):
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:
      IMX = MIN(LEN(STRIN),26)
      STROUT = ABET(1:IMX)
You could use embedded wrapit interface blocks, as in example 2 above, or use the following wrapit interface block:
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"
 cstr = new(26,character)        ;  create a character array of length 26
 EXAMPLE04_SO::ex04("fifteen letters",cstr)
 str = chartostring(cstr)
produces the output:
opening: ./ex04.so
Variable: str
Type: string
Total Size: 4 bytes
            1 values
Number of Dimensions: 1
Dimensions and sizes:   [1]

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.
      REAL X(M),Y(N),FXY(M,N)
C  Calculate FXY(I,J) = 2*I+J
      DO 10 J=1,N
        DO 20 I=1,M
          FXY(I,J) = 2.*REAL(I) + REAL(J)
   20   CONTINUE
      REAL A(M,N)
C  Print the array A by rows using an F6.1 format with
C  7 values per line.
      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 *,' '
Use the following wrapit interface block:
      REAL X(M),Y(N),FXY(M,N)
      REAL A(M,N)
to create the NCL wrapper function and the dynamic shared object (named ex05.so). Then the following NCL script:
external EX05 "./ex05.so"
; calculate three values of a quadratic equation
   m = 11
   n = 3
   x = new(m,float)
   y = new(n,float)
   fxy = new((/n,m/),float)
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.

NCL and object-oriented programming

When the GSUN functions and procedures may not be enough

The GSUN suite of functions and procedures described in the "Learning NCL by example" chapter provides a simplified interface to NCL. It does not, however, provide you with access to the full graphics capabilities of NCL. There are some situations where you may want to go beyond the simplified approach of the GSUN functions and procedures:

  1. To do multiple overlays (for example, a vector plot on a contour plot on a map plot)
  2. To allow more control over your plots
  3. To use the full capabilities of resource files
  4. To make your NCL script run faster
Using the full graphics capabilities of NCL requires that you understand the basic concepts behind object-oriented (OO) programming. This section is not intended to be an introduction to object-oriented programming, but rather it defines a few key OO concepts and describes how they relate to NCL. There is a non-technical book written by Dr. David A. Taylor called Object Technology - A Manager's Guide that we recommend if you want to learn the basic principles of object-oriented programming.

Introduction to OO terms

Some common terms that you encounter when OO programming is discussed in the context of NCL include: object, class, instance, inheritance, superclass, subclass, class hierarchy, and composite class. These terms are defined throughout this introduction and are mixed with NCL definitions that are related to these terms. Some of these terms may seem obscure at the moment, but once you read the section "Putting it all together" (which contains some actual examples), it should make more sense.

OO definition: object

An object is something that contains attributes and methods. The attributes describe the object, and the methods describe operations that can be performed on the object.

An object can be something concrete. For example, you can consider a car an object, and its attributes might be its color, its make, the year made, whether it's a 2-door or 4-door, etc. A method of the car object might be to describe how to move it from point A to point B.

An object can also be something more abstract. For example, in NCL a contour plot is considered an object, and its attributes (or what we call resources) are things like the 2-dimensional data to be contoured, the number of contour levels, the thickness of the contour lines, the fill patterns used for each contour level, the colors used for the contour lines, etc. One method of the contour plot object describes how to draw it to a workstation.

OO definition: class and instance

Since you will most likely have different kinds of cars (or contour plots), it is useful to have a template that describes what attributes and methods a certain object can have. This template is known as a class, and once you create an object from this template or class, it is called an instance of that class.

For example, going back to the car object, a class might be the specifications that a customer can choose for buying a new car. This would include some of the attributes listed earlier: the car's color, its make, whether it's 2-door or 4-door, and whether it has automatic or manual transmission. Once you decide on a 4-door white Honda Accord with automatic transmission, you call this object an "instance of the car class".

NCL definition: ContourPlot class

In NCL, the class for defining the attributes and methods for a contour plot object is known as a ContourPlot class. This class defines almost 200 attributes (resources) whose values you can set to create a particular contour plot object (an instance of the ContourPlot class). There are many other classes defined in NCL, and most of them are covered later.

OO definition: subclass, superclass, and inheritance

It is possible to have a class B that is a special case of another class A. In this case, class B is called a subclass of A, and A is called a superclass of B. The superclass passes its attributes and methods to its subclass in a concept known as inheritance. It is possible for a superclass to have multiple subclasses, and these subclasses in turn, can have subclasses.

For example, you could have a general "vehicle class" (a superclass) that defines a template for the different types of vehicles it can have, like a "truck class" and a "car class" (subclasses). A vehicle class might define things that can be specified for both cars and trucks, like their body color, their make, and whether they have manual or automatic transmission. The "truck class," in turn, would define a template for things that are specific to trucks, like the bed length and whether it has a cap, and the "car class" would define a template for things specific to cars, like whether or not it's a hatchback. The truck and car classes inherit the attributes from the vehicle class.

NCL definition: View class

In NCL, the ContourPlot class is a subclass of the View class. The View class contains classes that can be "viewed;" that is, that can be drawn to a workstation. Other subclasses of the View class include (but are not limited to) the XyPlot class, the VectorPlot class, the MapPlot class, and the StreamlinePlot class. These classes are mentioned in the examples in the "Learning NCL by example" chapter in the context of defining their resources. If you create an instance of one of these classes, then this object inherits the View class' resources, and you can set these resources when you set the object's specific resources.

Note: The subclasses listed above are not direct subclasses of the View class. For example, the ContourPlot class is a subclass of the DataComm class, which is a subclass of the Transform class, which is a subclass of the View class. It is possible to have several levels of subclasses, but for now, you don't need to worry about these other subclasses.

The View class defines resources that are common to all drawable classes, like where to draw instances of these classes in the viewport and what their width and height should be. You should already be familiar with some of these resources (vpXF, vpYF, vpWidthF, and vpHeightF) as they are used in many of the examples (see example 5). The subclasses of the View class, in turn, define resources that are specific to those classes. For example, cnFillOn is a resource specific to the ContourPlot class indicating whether or not contours should be filled, and xyLineColor is a resource specific to the XyPlot class indicating the color of the curves. Many of these specific resources appear in the examples as well.

OO definition: composite class

Classes that contain references to and manage other classes are called composite classes. For a composite class to "manage" another class means that it can send messages to the class it contains. For a class to contain another class means that it has access to the attributes and methods of the class it contains. Composite classes can contain other composite classes, and this nesting of composite classes can go to any level.

For example, in the car class described above, it could have an attribute for a stereo system. The stereo system could be considered an object in itself, whose attributes would be the brand, the number of speakers, whether it contains a CD player, etc. So, it makes sense to have a "stereo system class" that defines the attributes of a stereo system, and have it contained and managed by the car class (making the car class a composite class). The car class would "manage" where a stereo system object would be installed in a car object, which would vary depending on what kind of car object was created. The car class may contain other classes as well (like an "engine class"). Since the car class contains the stereo system class, this means that when a car object is created, you can specify the attributes of the stereo system object at the same time.

Note: It is important to understand the difference between composite classes and super/subclasses. If class A is a superclass of class B (making B a subclass of A), then B inherits A's attributes and methods. A does not manage B or send messages to it. If A contains B, however, then A is a composite class and A manages B by sending messages to it (and B is known as a composite member of A). Also, A has access to B's attributes and methods, but B does not have access to A's attributes and methods.

NCL definition: PlotManager class

In NCL, the ContourPlot, XyPlot, MapPlot, VectorPlot, and StreamlinePlot classes all contain the PlotManager class (in addition to other classes), so each one is considered a composite class. The PlotManger class contains the LabelBar class, the Legend class, the TickMark class, and the Title class, so it is a composite class as well.

The PlotManager class is a special class for "managing" the classes it contains on behalf of the classes that contain it. For example, whenever you create a ContourPlot object, tick marks are drawn automatically. This is because the PlotManager is managing the TickMark object by controlling where the tick marks and tick mark labels are drawn on the ContourPlot object. If the ContourPlot object is resized or repositioned, then the PlotManager resizes and/or repositions the TickMark object accordingly.

The PlotManager and its resources are discussed in more detail in example 2 when it is used to draw a LabelBar object with a ContourPlot object, and in example 8 when it is used to draw a Legend object with an XyPlot object.

Note: It is possible to independently create your own LabelBar object and other objects that are normally part of the PlotManager class. If you do this, however, then you need to manage these objects yourself, and this can be tedious if you reposition or resize your plot.

NCL definition: DataItem class

To create a ContourPlot object, you need to provide it with a data field to contour. Data fields have their own attributes, such as coordinate arrays, minimum and maximum values, and a missing data value, to name a few. It makes sense, then, to create a data field class from which data field objects can be created. The classes XyPlot, VectorPlot, and StreamlinePlot also require data fields, but of different types, so NCL defines a general DataItem class that has four subclasses to define the specific types of data field objects:

Though the View classes listed above reference DataItem classes, they do not manage DataItem classes, so the DataItem classes are not considered composite members of the View classes that reference them.

OO definition: class hierarchy

As noted in the definition for subclasses and superclasses, it is possible to have several levels of classes. These multi-levels form what's called a class hierarchy. A class hierarchy diagram is a useful way to understand how all of your classes relate to each other. Though they might seem confusing now, you might want to view the various class hierarchy diagrams for NCL classes at the URL:

NCL definition: Workstation class

As noted in the View class definition, the View class defines classes that can be drawn to a workstation (an X11 window, a PostScript file, or a metafile). Workstations have attributes of their own, like a color map which is an attribute common to all three types of workstations. They also have attributes specific to each type of workstation, like a file name (PostScript and metafile workstations only), an X color allocation scheme (X11 window workstation only), and orientation of the plot (Postscript workstation only). NCL defines a Workstation class that has three subclasses: an XWorkstation class, an NcgmWorkstation class, and a PSWorkstation class. The Workstation class passes its attributes to its subclasses, which means you can set the color map (and other Workstation resources) when you create an XWorkstation, NcgmWorkstation, or PSWorkstation object. Creating an object from one of these three classes provides you with a workstation in which to draw your View objects.

NCL definition: App class

The App class ("App" is just a shortened name for "Application") is a special class in NCL for managing resource files. An App object only needs to be created if you plan to have a resource file. If you don't create an App object, then one is created for you. The App object is discussed in more detail in the next section "Putting it all together."

NCL definition: Child/parent relationship between NCL objects

In addition to classes having relationships with each other via subclasses, superclasses, and composite classes, there's a child/parent relationship between objects once they are created. For example, if object B is a child of object A, then the following conditions apply:

The child/parent relationship is important in NCL, because whenever you create an object in NCL, you need to specify its parent.

Note: Do not confuse the child/parent relationship between objects with the subclass/superclass relationship between classes. They are independent concepts.

Putting it all together (six steps for creating a plot)

Now that the four main classes of NCL (App class, Workstation class, DataItem class, and View class) and the child/parent relationship between objects have been defined, you can apply this knowledge to writing NCL scripts that create objects from these classes to generate graphics. This section covers how to create objects from these four classes, how to change their resources, and how to put them together to create a plot.

The six basics steps for creating a plot are:

  1. Create an Application object (optional, only needed if you plan to use a resource file).
  2. Create a Workstation object.
  3. Create a DataItem object.
  4. Create a View object.
  5. Draw the View object.
  6. Advance the frame of the Workstation object.
In the GSUN suite of functions and procedures, steps 1-2 are handled by the function gsn_open_wks, and steps 3-6 are handled by one of the GSUN plotting functions (gsn_contour, gsn_xy, gsn_vector, etc.)

create statement (for steps 1-4)

Steps 1-4 involve the creation of an object, so if you want to use straight NCL code (versus using the GSUN functions and procedures), you need to use the create block statement to create each object. The basic template for the create statement is as follows:
  obj_id = create "obj__name" class_name parent_id
    "resource_name1" : resource_value1
    "resource_name2" : resource_value2
    . . .
  end create


The lines:
    "resource_name1" : resource_value1
    "resource_name2" : resource_value2
   . . .
from the create block statement above indicate a list of resources (attributes) that you can set for the object you are creating. Each resource name must be enclosed in double quotes, followed by a colon, followed by the resource value. This style of setting resources is identical to how you set resources in a setvalues block statement, which is covered in examples 9 and 11. Resources within an object creation call can be set in any order. There is more information about setting resources in the section "Resource files".

As noted in example 1, there are hundreds of resources you can set for various objects. Most of these resources have default values that are either hard-coded or set dynamically when you run NCL on one of your scripts. Some resources do not have default values, like those resources for setting data fields in a DataItem object, or for referencing a DataItem object from one of the View objects. You only need to set a resource value if you want to change its value from the default, or if it doesn't have a default value.

Note: There may be cases where you don't need to set any resources when you create an object, which means that your create call will look like this:

  obj_id = create "obj__name" class_name parent_id
  end create

Calling draw and frame (for steps 5 and 6)

Step 5 is accomplished by calling draw on the View object created, and step 6 is accomplished by calling frame on the Workstation object created.

NCL object examples

This section shows some actual examples of how to create objects in an NCL script and generate a plot from these objects. The examples here are identical to ones in the "Learning NCL by example" chapter. There are a couple of more advanced examples in the appendix. It is also recommended that you go to the "Examples: pick a utility" section of the Quick Start Guide which contains almost 100 NCL script examples.

NCL object example 1

The following example produces graphical output identical to frame 1 of example 1. It creates an XWorkstation object, a CoordArrays object, and an XyPlot object. An App object is not created in this example, because a resource file is not in use. This example NCL script is about as basic as they come, setting only the resources that are absolutely necessary to get an XY plot. The comments in the script explain what the various lines are doing.

  x = new(9,float)  ; Define two 1D arrays of 9 elements each.
  y = new(9,float)
  x = (/10.,20.,30.,40.,50.,60.,70.,80.,90./)
  y = (/0.,0.71,1.,0.7,0.002,-0.71,-1.,-0.71,-0.003/)

; Create a Workstation object to draw the XY plot to.  Note
; that no resources are being set here (the default values are
; acceptable in this case).  The parent is being set to "defaultapp"
; since no App object was created.
  wks_object = create "x11" xWorkstationClass defaultapp
  end create

; Create a CoordArrays (DataItem) object.  Set two resources for
; defining the X and Y arrays whose points you want to plot.
; "caXArray" and "caYArray" are examples of resources that don't
; have default values, so at least one of them must be set when a
; CoordArrays object is created. If only one of these resources
; is set, then the one that isn't set defaults to integer
; values from 0 to n-1, where "n" is the number of points in the
; one that is set.
  data_object = create "data" coordArraysClass defaultapp
    "caXArray" : x
    "caYArray" : y
  end create

; Create an XyPlot plot object.  Reference the CoordArrays object 
; created above (data_object) through the XyPlot resource "xyCoordData".
; Note that the parent is being set to the Workstation
; object identifier (wks_object).
  plot_object = create "plot" xyPlotClass wks_object
    "xyCoordData" : data_object
  end create

; Draw the XyPlot object (represented by the identifier
; "plot_object"). Since "wks_object" is the parent of
; "plot_object" in the above create call, "draw" draws
; the plot to this workstation.

; Advance the frame of the Workstation object (object
; identifier "wks_object").

NCL object example 2

The following example produces graphical output identical to frame 4 of example 2. It creates an XWorkstation object, a ScalarField object, and a ContourPlot object. An App object is not created in this example, because a resource file is not in use. This example NCL script shows how you can set a PlotManager resource for getting a label bar to be drawn with the contour plot. The comments in the script explain what the various lines are doing.

; Open a netCDF file from which to read data.
  cdf_file = addfile("$NCARG_ROOT/lib/ncarg/data/cdf/contour.cdf","r")

  Z    = cdf_file->Z(0,0,:,:) ; Read geopotential height, latitude, and
  lat  = cdf_file->lat        ; longitude values and store to local NCL
  lon  = cdf_file->lon        ; variables "Z", "lat", and "lon".

; Create an X11 workstation, using all of the default resources.
  xwks = create "x11" xWorkstationClass defaultapp end create

; Create a ScalarField data object to be referenced by the ContourPlot
; object created later.  When a ScalarField object is created, the 
; one resource that must be set is "sfDataArray".
; If "sfXArray" or "sfYArray" are not set, they default to integer
; values from 0 to m-1 and 0 to n-1, where m and n are the dimensions
; of the scalar field.
; "sfMissingValueV" is another resource that has no default value,
; and it only needs to be set if the data have missing values (as these
; data do).
  dataid = create "data" scalarFieldClass defaultapp
    "sfDataArray"     : Z            ; The 2D field to contour.
    "sfXArray"        : lon          ; The X coordinate array.
    "sfYArray"        : lat          ; The Y coordinate array.
    "sfMissingValueV" : Z@_FillValue ; The missing value value.
  end create

; Create a ContourPlot object. Reference the ScalarField object 
; created above (dataid) through the ContourPlot resource
; "cnScalarFieldData".
; There are several other contour resources being set here. Notice also
; that the PlotManager resource "pmLabelBarDisplayMode" is being set to
; "Always" to indicate that it should turn on the drawing of a label
; bar.  The PlotManager "manages" this label bar by controlling
; where it should be drawn with respect to the contour plot.  It also
; matches the colors in the label bar boxes with the colors in the
; filled contours, and labels them according to the data in the contour
; plot.
; Because the ContourPlot class is a composite class that contains the
; PlotManager class, which in turn contains the LabelBar and Title
; classes, then by definition you can set PlotManager, LabelBar, and
; Title resources when you create a ContourPlot object.
  cnid = create "contourplot" contourPlotClass xwks
    "cnScalarFieldData"     : dataid        ; Reference to the data object.
    "cnFillOn"              : True          ; Turn on contour level fill.
    "cnMonoFillColor"       : False         ; Use multiple colors.
    "cnLineLabelsOn"        : False         ; Turn off line labels.
    "cnInfoLabelOn"         : False         ; Turn off informational label.
    "cnLinesOn"             : False         ; Turn off contour lines.
    "pmLabelBarDisplayMode" : "Always"      ; Turn on label bar.
    "lbPerimOn"             : False         ; Turn off perimeter on label bar.
    "tiXAxisString"         : lon@long_name ; X axis title
    "tiYAxisString"         : lat@long_name ; Y axis title
    "tiMainString"          : Z@long_name   ; Main title
    "tiMainFont"            : 26            ; Main title font
    "tiXAxisFont"           : 26            ; X axis title font
    "tiYAxisFont"           : 26            ; Y axis title font
  end create

; Draw the ContourPlot object.  Note that the contours are filled in
; color, even though no color map has been specified.  This is
; because NCL provides a default color map.

; Advance the frame of the Workstation object.

For more examples of NCL scripts that use straight NCL code, see the appendix.


There are some common pitfalls you might run into when writing NCL scripts that create objects and set resources:
  1. When you use the PlotManager to control tick marks, titles, legends, or label bars for your plot, you need to be aware that some of the resources for these objects may be turned off by the PlotManager, meaning you won't be able to change their values. For example, if you use the PlotManager to turn on a label bar for a contour plot, you won't be able to set a resource called lbOrientation, which normally allows you to change the rotation of your label bar. The documentation for these resources indicate when the PlotManager may take control of it.

    If you need more control of these objects, then you always have the option of creating these objects directly (rather than turning them on via the PlotManager) and managing them yourself.

  2. When you create an XyPlot object, you cannot set resources at that time for changing aspects of the curve(s) like color, width, dash patterns, etc. You must first create the XyPlot object, use getvalues to retrieve something called the DataSpec object (via the XyPlot resource xyCoordDataSpec), and then set the desired resources using setvalues on the object(s) you retrieved. A separate DataSpec object will be returned for every DataItem object added to the XyPlot object (XyPlot is the only class that is allowed to have multiple DataItem objects), so if you add three DataItem objects to an XyPlot object, you will get an array of three DataSpec objects when you retrieve the value for the xyCoordDataSpec resource.

    For example, to make the curve color yellow in NCL object example 1 above, you would add the following code before the draw(plot_object) line:

      getvalues plot_object         ; Retrieve the DataSpec object.
        "xyCoordDataSpec" : dspec
      end getvalues
      setvalues dspec 
        "xyLineColor" : 5  ; Index 5 is yellow in the default color table.
      end setvalues
    For some more advanced examples on how the DataSpec object works, see XyPlot examples 6, 16, and 17 of the Quick Start Guide.

  3. If you are creating a plot and you want to put tick marks on the top or right axis independent of the tick marks on the bottom or left axis, then you need to set tmXUseBottom and/or tmYUseLeft to False in order for this to work.

Resource files

What are resource files?

Resource files are ASCII files containing lines of data in which values are assigned to resources. An NCL resource file allows you to set NCL resources in a manner similar to X11 resources that you set in .Xdefaults or .Xresources files.

For example, in the "Learning NCL by example" chapter, example 9 has a resource file associated with it called "gsun09n.res" that contains the lines:

*vpXF                        : 0.05
*cnFillOn                    : True
*mpProjection                : Stereographic
These resources change the X position of the plot in the viewport, turn contour fill on, and change the map projection to "Stereographic."

Resource files are loaded only once by the NCL interpreter, when NCL is initially executed. Any resources you set in the resource file that are misspelled or don't apply to the kind of plots you are creating are ignored. No error messages are printed, so you must be careful to spell the resource names correctly.

Note: Any resources set in a resource file can be overridden by setting them in your NCL script.

Why (or why not) to use resource files

There are many reasons why you may want to use a resource file rather than setting resources directly in your NCL script:
  1. They allow you to see all or most of your resources in one location. You can view your resources easier this way, since they are not interspersed with other lines in your NCL script.

  2. They may speed up the processing of your NCL script by the NCL interpreter, especially if you have lots of resources. This is because the NCL interpreter can generally load a resource file faster than it can process resources scattered throughout an NCL script.

  3. One resource file can apply to many NCL scripts, saving you the task of entering the same resource in multiple NCL scripts.

  4. A resource file that contains the line:

    *Font : name_of_font
    allows you to change all of the fonts in your plot to a particular font. There is currently no way to change all the fonts in your plot with one line inside an NCL script.

  5. Resource files can be used to create stylized plots.

There are also some reasons why you may not want to use a resource file:
  1. You may forget that you have a resource file, and spend a lot of time trying to figure out why your NCL script is behaving differently than you expect.

  2. Some people prefer to have their NCL code and resources in one place, rather than split between two files.

  3. If you misspell a resource in the resource file, you don't get an error message (like you do in an NCL script). This makes resource files difficult to debug.

How to create resource files

General format

The simplest way to set a resource in a resource file is to start off with an asterisk ("*"), immediately followed by the name of the resource, followed by a colon (":"), followed by the value of the resource. There must be no space between the asterisk and resource name, but you can have as many spaces as you want before and after the colon. Only one resource can be set per line, and comments begin with the "!" character. For example, in the following five lines:

! This is a comment.
*tiMainFont        : 25
*tiMainString      : This is a title
*mpFillOn          : True
*cnLevelSpacingF   : 0.10
the first line is a comment, and the next four lines set the resources for changing the title font to 25 ("times-roman"), creating a title, turning map fill on, and setting the contour level spacing to 0.10. Note that the string "This is a title" does not require double quotes around it as it would in an NCL script. If you put quotes around the string, then the quotes will appear as part of the title.

Resource types

Resources can have four different types: float, integer, string, and logical, and can be scalars or multi-dimensioned arrays.

Setting scalar resources

To set a scalar resource, succeed the ":" character with the value of the resource as a float, integer, string, or logical. You've already seen how to do this in the example above:

*tiMainFont        : 25
*tiMainString      : This is a title
*cnLevelSpacingF   : 0.10
*mpFillOn          : True
The first resource is being set with the integer "25", the second resource with the string "This is a title", the third resource with the float "0.10", and the fourth resource with the logical "True".

The number "25" that is being used to set the font is an enumerated value (an integer that represents a predefined string). When used with a font resource, the number "25" is an index into a font table and represents the font "times-roman". This resource could also be set with:

*tiMainFont        : times-roman
Again, note that there are no quotes around "times-roman". If you had put quotes around this resource:

*tiMainFont        : "times-roman"
then you would have gotten an error message that looks like the following:

   fatal:NhlCvtStringToEnum: Unable to convert string ""times-roman"" to requested type 
Logical values can be set with the strings True or False, or the integers 0 or 1. Both True and False must be capitalized and must not have quotes around them.

Setting array resources

To set a resource that takes a multi-dimensional array as its value, use "(/" and "/)" around the array values in the same manner that you would in an NCL script.

For example, to set the resource xyExplicitLabels to a 1-dimensional string array, do the following:

*xyExplicitLabels:  (/curve 1, curve 2, curve 3, curve 4/)
To set the resource wkColorMap to a 15 x 3 float array, do the following:
*wkColorMap:   (/(/ 0.00, 0.00, 0.00 /),(/ 0.66, 0.66, 0.66 /),\
                 (/ 0.40, 0.40, 0.40 /),(/ 0.00, 1.00, 1.00 /),\
                 (/ 0.20, 0.56, 0.80 /),(/ 0.00, 0.00, 1.00 /),\
                 (/ 0.50, 0.00, 1.00 /),(/ 1.00, 0.00, 1.00 /),\
                 (/ 0.14, 0.56, 0.14 /),(/ 0.00, 1.00, 0.00 /),\
                 (/ 1.00, 1.00, 0.00 /),(/ 0.86, 0.58, 0.44 /),\
                 (/ 0.65, 0.16, 0.16 /),(/ 1.00, 0.50, 0.00 /),\
                 (/ 1.00, 0.00, 0.00 /)/)
Note that the "\" character is used as a continuation line in a resource file.


In a resource file, the asterisk in front of the resource name serves as a wildcard character. In other words, the line:

*cnFillOn : True
causes contour fill to be turned on for all contour plots in an NCL script to which the resource file is applied, unless cnFillOn is set to False for a particular contour plot within the NCL script. Resources set in an NCL script override resources set in an NCL resource file.

You can force resources in a resource file to only apply to certain plots by replacing the wildcard asterisk character with "qualifiers". Whenever you call a GSUN function that creates a plot, a name is given internally to that plot (in addition to the function returning a reference to the plot), and this name can be used as a qualifier in a resource file. The name given to a plot created by a call to a GSUN function is the second string passed to gsn_open_wks, followed by an underscore ("_"), followed by a string indicating type of plot being created.

For example, if you create a contour plot and a vector plot with the following calls:

  . . .
  wks = gsn_open_wks("x11","myplot")
  . . .
  contour = gsn_contour(wks,cdata,cnresources)
  vector  = gsn_vector(wks,vdata,vcresources)
  . . .
then the function gsn_contour assigns the name "myplot_contour" to the contour plot, and the function gsn_vector assigns the name "myplot_vector" to the vector plot. If you call a GSUN function that creates two plots, like gsn_contour_map, then this function assigns the name "xxx_contour" to the contour plot and "xxx_map" to the map plot, where xxx is the second string passed to gsn_open_wks.

For example, in the following two lines in the resource file for example 9:

*gsun09n_contour.pmLabelBarDisplayMode : Always
*gsun09n_map.pmLabelBarDisplayMode     : Never
the strings "gsun09n_contour" and "gsun09n_map" are internal names assigned by gsn_contour_map to the contour and map plots. These strings are used as qualifiers to force a label bar to be drawn for the contour plot and not for the map plot. If you had just set:

*pmLabelBarDisplayMode : Always
then you would have gotten a label bar with both the map plot and the contour plot.

You can fully qualify a resource to the point where there are no wildcard characters. Here's a basic template for what a fully-qualified resource name would look like in a resource file:

application_name.workstation_name.plot_name.resource_name : resource_value
In the GSUN suite of functions, application_name is the second string passed to gsn_open_wks, and workstation_name is the same string with "_x11", "_ps", or "_ncgm" appended, depending on what kind of workstation was opened in the call to gsn_open_wks.

If you are using create to create your application, workstation, and plot objects as described in the section "NCL and object-oriented programming", then the qualifying names are whatever names you give your objects (the string after the create keyword).

Any place where you don't want to use one of the qualifying names, you can just replace it with a wildcard character. For example, let's assume you opened two workstations with the following lines:

  xwks = gsn_open_wks("x11","example")
  pswks = gsn_open_wks("ps","example")
Let's further assume you want to draw a contour plot, but that you only want to have a title drawn with it in the PostScript file. Then, you would have an entry in your resource file that looks like this:
*example_ps*tiMainString   : This is a title
The string "example_ps" is the name given to the PostScript workstation by the call to gsn_open_wks. Note that a wildcard character appears between this qualifier and the resource name. This is because normally the qualifier plot_name would appear after the workstation name, but since you don't need it in this case, you can replace it with a wildcard character.

The fully qualified path of the above resource would be:

example.example_ps.example_contour.tiMainString  : This is a title
The documentation for the GSUN functions and procedures contains information about the internal names given to the various plots, workstations, and data objects.

It is important to note that this internal naming scheme of the GSUN functions and procedures is somewhat limiting. For example, if you create two separate contour plots with a call to gsn_contour and they are both being drawn to the same workstation, then they will both have the same internal name. This means that if you want to set the same resource for both of them, but with different values, you can't do it from a resource file because there is no way to distinguish the two contour plots from each other. Instead, you have to set the resource for both plots in the NCL script.

For more information on setting resources in a resource file, see the section "Understanding resources" in the NCAR Graphics 4.1 User Guide.

Where to put resource files

By default, the NCL interpreter looks for the resource file in the same directory as where NCL is being executed from. In the GSUN suite of functions, if you want to change this default directory, create an attribute of the second string passed to gsn_open_wks called "appUsrDir" and set its value to whatever directory you want NCL to look in for the resource file. For example, if your NCL script is in "/usr/home/smith/scripts/plotxy.ncl", and your resource file is in "/usr/home/smith/resfiles/plotxy.res", then set appUsrDir as follows in the "/usr/home/smith/scripts/plotxy.ncl" file:
  . . .
  resname = "plotxy"
  resname@appUsrDir = "/usr/home/smith/resfiles"
  wks = gsn_open_wks("x11",resname)
  . . .
Remember, the NCL interpreter looks for a resource file by the name resname.res, where resname is the second argument (a string) passed to gsn_open_wks.

It is possible to have up to four resource files applied to a single NCL script. For more information, see the section "Using resource files" in the NCAR Graphics 4.1 User Guide.

More information in general on resource files appears in the "Resource database" section of the NCAR Graphics 4.1 Reference Manual and in the "Understanding resources" and "Resource types" sections of the NCAR Graphics 4.1 User Guide.

home | toc | about doc | intro | examples | basics | beyond basics | hints and tips | appendix | glossary