4.1 Dimensionality and Bounds

4.2 Dimension Sizes

4.3 NDF Size

4.4 “Safe” Dimension Sizes under Error Conditions

4.2 Dimension Sizes

4.3 NDF Size

4.4 “Safe” Dimension Sizes under Error Conditions

This section describes the attributes which determine an NDF’s shape and size, and shows how the values of these attributes can be obtained and used.

An NDF’s *shape* is determined by its *dimensionality* (*i.e.* its number of dimensions) and the lower and
upper bound of each dimension. In this respect, an NDF is exactly like a Fortran 77 array. It may have
between 1 and 7 dimensions, each indexed by an integer lying between the lower and upper
*pixel-index bounds* of that dimension. These bounds may take any integer values, so long as
the lower bound does not exceed the upper bound. Each element of the array is termed a
*pixel*.

All the array components of an NDF (*i.e.* its *data*, *variance* and *quality* components) have the same
shape, so an NDF may either be regarded as a set of up to three matching N-dimensional arrays, or as
an N-dimensional array of pixels each of which has a *data* (and optionally a *variance* and *quality*)
value associated with it. In general, other non-array components may also be present, of
course.

The usual notation can be used to refer to an NDF’s shape. For instance:

(0:255, -1:1, 3)

describes the shape of a 3-dimensional NDF with pixel indices ranging from 0 to 255 in the first dimension, from $-$1 to 1 in the second dimension, and from 1 to 3 in the third dimension. The lower bound is taken to be 1 if not specified.

An NDF’s pixel-index bounds and dimensionality may be determined from its identifier with the routine NDF_BOUND, as follows:

INTEGER NDIMX, LBND( NDIMX ), UBND( NDIMX ), NDIM

...

CALL NDF_BOUND( INDF, NDIMX, LBND, UBND, NDIM, STATUS )

...

CALL NDF_BOUND( INDF, NDIMX, LBND, UBND, NDIM, STATUS )

Here, the integer arrays LBND and UBND are supplied to receive the lower and upper bounds of each dimension, and the number of dimensions is returned via the NDIM argument. The size of the LBND and UBND arrays is specified by the NDIMX argument and should normally be sufficiently large to ensure that all the NDF’s bounds can be accommodated. The symbolic constant NDF__MXDIM (defined in the include file NDF_PAR) specifies the maximum number of dimensions which an NDF may have, and is often used when declaring the size of such arrays.

If the value of NDIMX is larger than the actual number of NDF dimensions, then any unused elements in the LBND and UBND arrays will be filled with the value 1. This allows an application to “pretend” that an NDF has more dimensions than it actually has, if that is convenient. For instance, suppose an application expects a 2-dimensional NDF and determines the pixel-index bounds as follows:

INTEGER LBND( 2 ), UBND( 2 ), NDIM

...

CALL NDF_BOUND( INDF, 2, LBND, UBND, NDIM, STATUS )

NDIM = 2

...

...

CALL NDF_BOUND( INDF, 2, LBND, UBND, NDIM, STATUS )

NDIM = 2

...

Although this application simply asserts that the NDF is 2-dimensional, it will nevertheless be able to handle 1-dimensional NDFs as well, because they will appear to the rest of the application as if their second dimension has pixel-index bounds (1:1).

Sometimes, this deception can also be practised if the NDF has more dimensions than expected. In this
case, NDF_BOUND will return the expected number of pixel-index bounds and will discard the
bounds information for any extra dimensions which are non-significant (*i.e.* for which the lower
and upper bounds are equal). In the above example, the NDF would therefore appear
2-dimensional to the rest of the application. However, if any of the extra dimensions are
significant (*i.e.* have unequal lower and upper bounds), then NDF_BOUND will report an error
explaining the problem and return with its STATUS argument set to NDF__XSDIM (too
many dimensions), as defined in the include file NDF_ERR. In the normal course of events,
the application would then terminate and the error message would be delivered to the
user.

The above simple program fragment will therefore ensure that an NDF is made to appear 2-dimensional if at all possible, and will correctly handle any difficulties which may arise.

In many applications, knowledge of the pixel-index bounds of an NDF may be unnecessary and it is
sufficient to know the dimension sizes (*i.e.* how many pixels there are along each dimension). This
will normally be the case when converting an application that was originally written for a
different data system, where typically the dimension size is the only shape information
used.

The dimension size D is related to the lower and upper bounds of each dimension, L and U, by:

$D=U-L+1$

but may be obtained more directly using the routine NDF_DIM, as follows:

As with NDF_BOUND, an integer array DIM is provided to receive the returned dimension sizes, and
any unused elements in this array will be assigned the value 1. If the array size NDIMX is less
than the actual number of NDF dimensions, then information for extra non-significant
dimensions (*i.e.* dimensions with sizes equal to 1) will be discarded. However, if any of the
discarded dimensions would be significant (*i.e.* have a size greater than 1), then an error will
result.

This again allows an application to make an NDF appear to have the number of dimensions it requires if at all possible. For instance, a smoothing application might expect an NDF to contain a 2-dimensional image, and could obtain the dimension sizes it requires, NX and NY, as follows:

INTEGER DIM( 2 ), NDIM, NX, NY

...

CALL NDF_DIM( INDF, 2, DIM, NDIM, STATUS )

NX = DIM( 1 )

NY = DIM( 2 )

...

...

CALL NDF_DIM( INDF, 2, DIM, NDIM, STATUS )

NX = DIM( 1 )

NY = DIM( 2 )

...

No explicit check on the number of NDF dimensions would be necessary.

The *size* of an NDF is the total number of pixels lying within its bounds, and is given by the
product of the sizes of all its dimensions. Many applications require only this information
about an NDF’s shape and can conveniently obtain it using the routine NDF_SIZE, as
follows:

This routine returns the total number of pixels via its integer argument NPIX. Note, however, that this same quantity is also returned as a by-product of most routines which directly access the array components of an NDF (see §8.2, for example), so a call to NDF_SIZE can often be avoided.

A common pitfall which can affect software using the inherited status error-handling strategy employed by all the NDF_ routines (see §2.3) arises from the way in which the Fortran 77 “adjustable array” facility behaves.

Consider the following example, where a call to the NDF_SIZE routine is used to obtain the number of pixels in an NDF, and the returned result is then passed to another routine where it specifies the size of an adjustable array, A:

Normally, this will cause no problems. However, if there has been a previous error so that STATUS is
no longer set to SAI__OK, then NDF_SIZE will not execute. If no further precautions were taken, the
returned value of NPIX would be undefined and could easily have a value (zero, perhaps) which is
invalid as an adjustable array dimension. In such cases, the application would probably terminate
(*i.e.* crash) with a run-time error.

To prevent this happening, all NDF_ routines which return array size or dimensionality information will provide a “safe” value of 1 under error conditions. This applies both to the case where STATUS is set before the routine is called, as well as to the case where the NDF_ routine itself fails. This generally means that no extra precautions need to be taken to prevent run-time errors from this cause.