Expressions and operators

Data types

Numeric data types

There are six numeric data types in NCL: double, float, long, integer, short, byte. The float type is the basic type for representing floating point numbers; double will provide the same or more precision than float; integer is the basic type for representing integral values; short will have the same or less precision as integer; byte will almost always be an 8-bit quantity. The algebraic operators operate on the numeric data types.

Non-numeric data types

The following table summarizes the non-numeric data types in NCL:

---------------------------------------------------------------- 
 Data type         Description
---------------------------------------------------------------- 
 character         Used to store a single character
 string            A finite sequence of characters
 logical           Can have one of two values: True or False
 graphic           References an instance of an HLU object
 file              References a disk file (returned by addfile)
---------------------------------------------------------------- 

Coercion of types

For an expression to be evaluated correctly, it is often necessary to convert one of the basic data types into another. This processes is called "coercion" and it is often done automatically. For example, when a float is multiplied by an integer, the integer will be converted to a float, the multiplication performed, and a float calculated for the expression value. The following table shows how the type coercions are performed:


 Type             Coercible to
---------------------------------------------------------------- 
 character        string
---------------------------------------------------------------- 
 short            character
                  string
                  integer
                  long
                  logical
                  float
                  double
--------------------------------------------------------------- 
 integer          string
                  long
                  logical
                  float
                  double
---------------------------------------------------------------- 
 long             logical
                  string
                  float
                  double
---------------------------------------------------------------- 
 float            logical
                  string
                  double
---------------------------------------------------------------- 

Creating data

All numeric data types, strings, characters, logical, and graphic types can be created using the "new" statement. There are two types of new calls: one creates an array of data with a specific missing value assigned to each element; the other creates the array of data using the default missing value. The new function takes, as parameters, an array of dimension sizes, the type keyword to create, and (optionally) a missing value to assign to each element of the new data array. The following creates a three-dimensional float array:

a = new((/5,6,7/),float)
The following is an example of how to assign a specific missing value.

a = new((/5,6,7/),float,-1e12)
File data types are created differently. A file type is created only by the addfile function. Arrays of type graphic can be created with new, but none of the elements of the array will actually reference an existing HLU object. Each element will contain a missing value. In addition to new, there are ways of entering data manually. Currently there are four types of literal values. Floating point numbers must be entered with a decimal point. The following show creation of data using literal floating point values:

a = 2.
a = 1.2
a = (/ 1., 2., 3. /)
Integer values are specified without the decimal point:

a = 2
a = (/ 1, 2, 3 /)
String values are specified as characters enclosed in double quotes ("):

a = "NCAR Graphics"
Logical values are specified using the keywords "True" and "False":
a = False

NCL operators

Summary table

The following table summarizes the primary operators in NCL. The operators are presented in their order of precedence, that is, in the evaluation of an expression, if one operator appears above another in the table, it will be performed first. See the examples of expression evaluation below. If a non-associative operator (such as "^" or "%") occurs successively, then the precedence proceeds left-to-right. For example, 2^3^4 evaluates to 4096.


---------------------------------------------------------------- 
 Operator         Description                   Type
---------------------------------------------------------------- 
    ->            File variable                 metadata
---------------------------------------------------------------- 
    @             Attribute                     metadata
    &             Coordinate variable           metadata
    !             Dimension name                metadata
---------------------------------------------------------------- 
    -             Unary negation                algebraic
---------------------------------------------------------------- 
    ^             Exponentiation                algebraic
---------------------------------------------------------------- 
    *             Multiplication                algebraic
    /             Division                      algebraic
    %             Modulus                       algebraic
---------------------------------------------------------------- 
    +             Addition                      algebraic
    -             Subtraction                   algebraic
---------------------------------------------------------------- 
    <             Less than selector            algebraic
    >             Greater than selector         algebraic
---------------------------------------------------------------- 
  .not.           Logical negation              logical
  .le.            Less than or equal to         relational
  .lt.            Less than                     relational
  .ge.            Greater than or equal to      relational
  .gt.            Greater than                  relational
  .ne.            Not equal to                  relational
  .eq.            Equal to                      relational
  .and.           And                           logical
  .or.            Or                            logical
  .xor.           Exclusive or                  logical
---------------------------------------------------------------- 
   =              Assignment                    assignment
---------------------------------------------------------------- 

Detailed descriptions of operators:

File variable (->)
This operator requires two operands. The first operand is a file variable (as created with an addfile command). The second operand is a variable name. The specified variable is either set or retrieved as appropriate. The file variable operator differs from other operators in that the operands are not expressions but identifiers.

Examples:

       file1->a = 5
       file1->a@value = 22.
       print(file1->var1)
Attribute (@)
This operator takes two operands. The first operand is a variable name, and the second operand is an attribute name for the variable in the first operand. The result can be any type. The attribute operator is similar to the file variable operator in that its operands also must be identifiers and not expressions.

Examples:

     a@units = "Degrees"
     a@value = 3.14159
     print(a@units)
Coordinate variable (&)
This operator requires two operands. The first operand is a variable name, and the second operand is the name of a dimension of the first operand. The result is a coordinate variable. The values in the coordinate variable must be monotonically increasing or decreasing. If the value assigned is non-monotonic or contains missing values, the assignment still occurs but a WARNING message is generated and attempts to use coordinate subscripting will fail with a FATAL message. The coordinate variable operator only accepts identifiers just like the attribute and file variable operators.

Example:

         a&Dimension1 = (/.1,.2,.3,.4,.5,.../)
Dimension name (!)
This operator takes two operands. The first operand is either a variable or a file variable, and the second operand is an integer. The second operand indicates the dimension number (starting with 0). The result is of type string. This operator is used to retrieve or set names for a variable's dimension(s).
Unary negation (-)
This operator operates on a single operand and that operand must have a numeric type. If the operand is an array, the negation will be applied individually to each element of the array.
Exponentiation (^)
This operator requires two operands. The first operand is raised to the power of the second operand. The operands must be of numeric type. If the first operand is negative, then the second operand must be positive. If either operand is a non-linear array, then the other operand must be a scalar or an array of the same shape. An array raised to a scalar power results in an array of the same size with each element of the original array raised to the scalar power. A scalar raised to an array power will result in array of the same shape as the power where each element of the array is the scalar raised to the power of the array element. An array raised to an array power results in the obvious thing. The result of the exponentiation operator is of type double.
Multiplication (*)
This operator requires two operands. The first operand is multiplied by the second operand. The operands must be of numeric type. Array operands are handled in a manner analogous to that described in exponentiation.
Division (/)
This operator requires two operands. The first operand is divided by the second operand. The operands must be of numeric type. If both operands are of type integer, then the result will be an integer where any decimal remainders are truncated. Array operands are handled in a manner analogous to that described in exponentiation.
Modulus (%)
This operator requires two operands of type integer. The result is an integer type that is the integral remainder of dividing the first operand by the second. Array operands are handled in a manner analogous to that described for exponentiation.
Addition (+)
This operator requires two operands. The first operand is added to the second operand. The operands must both be of numeric type or both of string type. If both operands are of numeric type, then the result will be the sum of the two operands; if both operands are of string type, then the result will be the concatenation of the two strings. Array operands are handled in a manner analogous to that described for exponentiation.
Subtraction (-)
This operator requires two operands. The second operand is subtracted from the first operand. The operands must both be of numeric type. Array operands are handled in a manner analogous to that described for exponentiation.
Less than selector (<)
This operator requires two operands. The result is the smaller of the values in the first operand and the second operand. If one operand is a scalar and the other is an array, then, for each array entry, the smaller of the array element and the scalar is selected for each element of the resultant array. The operands must either both be type numeric, or both type string. For strings string1 and string2, "string1 < string2" yields string1 if the first character of string1 is less than the first character of string2 in the ASCII collating sequence.
Greater than selector (>)
This operator requires two operands. The result is the larger of the values in the first operand and the second operand. If one operand is a scalar and the other is an array, then, for each array entry, the larger of the array element and the scalar is selected for each element of the resultant array. For strings string1 and string2, "string1 > string2" yields string1 if the first character of string1 is greater than the first character of string2 in the ASCII collating sequence.
Logical negation (.ne.)
This operator requires a single logical operand and returns a logical result. If the operand is True, then the result is False; if the operand is False, then the result is True.
Less than or equal to (.le.)
This operator requires two operands of numeric type. The two operands can be a scalar and an array, or two arrays of the same shape. If a scalar is being compared to an array, then an array of logical values is returned as per individual comparisons of the scalar with each array element. If two arrays are being compared, then an array of logical values is returned as per comparing each of the elements in one array with those in the other.
Less than (.lt.)
This operator requires two operands of numeric type. The two operands can be a scalar and an array, or two arrays of the same shape. If a scalar is being compared to an array, then an array of logical values is returned as per individual comparisons of the scalar with each array element. If two arrays are being compared, then an array of logical values is returned as per comparing each of the elements in one array with those in the other.
Greater than or equal to (.ge.)
This operator requires two operands of numeric type. The two operands can be a scalar and an array, or two arrays of the same shape. If a scalar is being compared to an array, then an array of logical values is returned as per individual comparisons of the scalar with each array element. If two arrays are being compared, then an array of logical values is returned as per comparing each of the elements in one array with those in the other.
Greater than (.gt.)
This operator requires two operands of numeric type. The two operands can be a scalar and an array, or two arrays of the same shape. If a scalar is being compared to an array, then an array of logical values is returned as per individual comparisons of the scalar with each array element. If two arrays are being compared, then an array of logical values is returned as per comparing each of the elements in one array with those in the other.
Not equal to (.ne.)
This operator requires two operands of any type and returns a result of type logical. If a scalar is being compared to an array, then an array of logical values is returned as per individual comparisons of the scalar with each array element. If two arrays are being compared, then an array of logical values is returned as per comparing each of the elements in one array with those in the other.
Equal to (.eq.)
This operator requires two operands of any type and returns a result of type logical. If a scalar is being compared to an array, then an array of logical values is returned as per individual comparisons of the scalar with each array element. If two arrays are being compared, then an array of logical values is returned as per comparing each of the elements in one array with those in the other.
And (.and.)
This operator requires two operands of type logical and returns a result of type logical. The result is True if and only if both operands are True.
Or (.or.)
This operator requires two operands of type logical and returns a result of type logical. The result is True if and only if either of the operands is True.
Exclusive or (.xor.)
This operator requires two operands of type logical and returns a result of type logical. The result is True if and only if one, but not both, of the operands is True.
Assignment (=)
The operator requires two operands. The value of the second operand is assigned to the first operand. The type of the second operand must be coercible to the type of the first operand.

NCL Expressions

Definition and examples

An NCL expression is any sequence of operators and operands that results in the computation of a value. In particular, any literal value is an expression, and any variable is an expression. Also, arrays are expressions as well as functions and procedures. Operators applied to expressions are expressions.

Parentheses surrounding an expression result in an expression and can be used to alter the order of evaluation in expressions in the same manner that they are used in most all programming languages. So, for example,

a + b / c
would be evaluated by dividing b by c and then adding a, whereas

(a + b)/ c
would be evaluated by adding b and a and then dividing by c.

Here are some examples of expressions:

.5
v1
sin(3.1415926)
a(2,3,1)^(b-1) + (file1->a)/2.718
String3 = "String1" + "String2"
(/2,8,11/) % (/2,3,4/)
((v1.lt.v3) .xor. (v2.lt.v4))

Missing data in the evaluation of expressions

If a variable is created and no valid value has been assigned to that variable, then that variable will have an attribute named "_FillValue", and the value of the variable will be set to the value of "_FillValue". A pre-defined value of the same type as the variable will be assigned to "_FillValue" unless a value has been specifically assigned by the user. Consider the following NCL session:

ncl 0> a = new(2,float)
ncl 1> b = (/ 3., a(0) /)
ncl 2> print(b)
Variable: b
Type: float
Total Size: 8 bytes
            2 values
Number of Dimensions: 1
Dimensions and sizes:   [2]
Coordinates: 
(0)     3
(1)     -999
ncl 3> c = (/ 5., 8. /) + b
ncl 4> print(c)
Variable: c
Type: float
Total Size: 8 bytes
            2 values
Number of Dimensions: 1
Dimensions and sizes:   [2]
Coordinates: 
(0)     8
(1)     -991
The variable "a" is created using the new command and, since a has not been assigned any value, it receives the default _FillValue of -999. This value for a is used in the creation of b, and this becomes the actual value of the second element of b. This value is used in the computation of c.

If the variables making up an expression contain more than one "_FillValue" value, the _FillValue of the left-most variable that contains a missing value is used for the value of the expression.

Expressions and metadata.

The handling of metadata in the computation of expressions presents problems. For example, consider a case where one file named "east" contains a vector variable U and another file named "north" contains a vector variable V. Suppose U has an attribute "long_name" that is set to "east wind component" and V has an attribute called "long_name" that is set to "north wind component". Then, suppose we want to calculate the wind speed and store the result in a variable c:

c = sqrt(east->U^2 + north->V^2)
Clearly, neither of the values for the attribute "long_name" applies to c. Similarly, attributes like valid_range, valid_min, valid_max, and scale_factor could not be correctly propagated to c. NCL solves this problem simply by not propagating metadata in the evaluation of an expression unless that expression is a single variable. This rule also applies to variables used as parameters to functions.

Examples of expression evaluation.

      
---------------------------------------------------------------- 
 Expression                    Evaluates as
---------------------------------------------------------------- 
 a < b + c                     a < (b + c)
 a/b*d%c                       ((a/b)*d) % c
 a^b*-c/d+e%f<g                ((((a^b)*(-c))/d) + (e % f)) < g
 a+b.lt.c*d                    (a+b) .lt. (c*d)
 a.le.c.or.d.gt.e.and.f.ne.g   (a.le.c).or.((d.gt.e).and.(f.ne.g))
 file2->a^3+file2->b*5         ((file2->a)^3) + ((file2->b)*5)
---------------------------------------------------------------- 



User Guide Control Panel

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


$Revision: 1.9 $ $Date: 1998/06/15 22:08:54 $