Routines for Accessing the
Extensible N-Dimensional Data Format
- TOC ↑
This section presents an overview of what NDF data structures are, and the facilities which the NDF_
system provides for manipulating them.
2.1 Overview of an NDF
The simplest way of regarding an NDF is to view it as a collection of those items which
might typically be required in an astronomical image or spectrum. The main part is an
N-dimensional array of data (where N is 1 for a spectrum, 2 for an image, etc.), but this may
also be accompanied by a number of other items which are conveniently categorised as
| Character components: || TITLE — ||NDF title
| || LABEL — ||Data label |
| || UNITS — ||Data units
| || || |
| Array components: || DATA — ||Data pixel values
| || VARIANCE — ||Pixel variance estimates |
| || QUALITY — ||Pixel quality values
| || || |
| Miscellaneous components: || AXIS — ||Coordinate axes
| || WCS — ||World coordinate systems
| || HISTORY — ||Processing history
| || || |
| Extensions: || EXTENSION — ||Provides extensibility |
The names of these components are significant, since they are used by the NDF
access routines to identify the component(s) to which certain operations should be
The following describes the purpose and interpretation of each component in slightly more
- – This is a character string, whose value is intended for general use as a heading for
such things as graphical output; e.g. ‘M51 in good seeing’.
- – This is a character string, whose value is intended to be used on the axis of graphs to
describe the quantity in the NDF’s data component; e.g. ‘Surface brightness’.
- – This is a character string, whose value describes the physical units of the quantity
stored in the NDF’s data component; e.g. ‘J/(m2Angs)’.
- – This is an N-dimensional array of pixel values representing the spectrum, image,
etc. stored in the NDF. This is the only NDF component which must always be present.
All the others are optional.
- – This is an array of the same shape and size as the data array, and represents the
measurement errors or uncertainties associated with the individual data values. If present,
these are always stored as variance estimates for each pixel.
- – This is an array of the same shape and size as the data array, and holds a set of
unsigned byte values. These are used to assign additional “quality” attributes to each
pixel (for instance, whether it is part of the active area of a detector). Quality values
may be used to influence the way in which the NDF’s data and variance components are
processed, both by general-purpose software and by specialised applications.
- – This component name represents a group of axis components which may be used to
describe the shape and position of the NDF’s pixels in a rectangular coordinate system.
The physical units and a label for each axis of this coordinate system may also be
stored. (Note that the ability to associate extensions with an NDF’s axis coordinate system,
although described in SGP/38, is not yet available via the NDF access routines described
- – This component may be used to hold information about any “world coordinate systems”
associated with the NDF. These may include celestial coordinate systems, such as right
ascension and declination (in various flavours), but may also represent other coordinates,
Multiple coordinate systems may be present.
The WCS component is a rather more complex entity than most other NDF components
and a full description is currently beyond the scope of this document. It stores world
coordinate information in a format defined by the AST library (see SUN/210) and known
as a “FrameSet”. You should consult SUN/210 for a full description of the facilities which
a FrameSet provides. The NDF_ library simply provides routines for reading and writing
this information (see NDF_GTWCS and NDF_PTWCS).
- – This component may be used to keep a record of the processing history which
the NDF undergoes. If present, this component should be updated by any applications
which modify the data structure.
- are user-defined HDS structures associated with the NDF, and are used to
give the data format flexibility by allowing it to be extended. Their formulation is not
covered by the NDF definition, but a few simple routines are provided for accessing and
manipulating named extensions, and for reading and writing the values of components
stored within them.
2.2 Overview of the NDF_ Routines
The NDF access routines described in this document all have names of the form:
identifies the operation which the routine performs. These routines provide facilities for performing
the following types of operation on NDF data structures:
- Obtaining access to NDFs, for both input and output.
- Creating and deleting NDFs.
- Enquiring about the attributes of NDFs, including their shape and size.
- Enquiring about the attributes of NDF components.
- Reading, writing and resetting NDF component values.
- Enquiring about (and flagging) the presence of bad pixels in NDF components.
- Accessing and handling quality information associated with NDFs.
- Modifying the attributes of NDFs (including their shape and size) and the attributes of
their components (such as their numeric type).
- Reading, writing and resetting the values of axis arrays and other axis components
associated with NDFs.
- Modifying the attributes of NDF axis components (such as the numeric type of axis
- Controlling the propagation of NDF components to output data structures.
- Creating, deleting and enquiring about NDF extensions, and obtaining access to
components stored within extensions.
- Controlling the propagation of NDF extensions to output data structures.
- Selection and management of sections which refer to subsets or super-sets of NDFs.
- Merging the attributes of NDFs to match the processing capabilities of specific
- Importing and finding NDFs held in HDS container files and copying of NDFs between
different HDS locations.
- Recording, accessing and deleting information about the processing history of an NDF.
- Constructing messages about NDFs.
- Controlling access to NDFs.
A full description of each routine can be found in Appendix D of this document.
2.3 Error Handling
The NDF_ routines adhere throughout to the standard error-handling strategy
described in SUN/104. Most of the routines therefore carry an integer inherited status
argument called STATUS and will return without action unless this is set to the value
when they are invoked. When necessary, error reports are made through the ERR_ routines in the
manner described in SUN/104. Where exceptions to this general behaviour exist, they are noted in the
appropriate subroutine descriptions in Appendix D.
2.4 Overview of a Typical Application
The following contains an example of a simple application which uses the NDF_ routines to add the
data arrays of two NDF data structures to produce a new NDF. This is not quite the simplest “add”
application which could be written, but is close to it. Nevertheless, it will do a good job, and will
respond correctly to unforseen circumstances or conditions which it is not designed to handle by
issuing sensible error messages.
The intention here is simply to give a flavour of how the NDF_ routines are used, so don’t worry if
you don’t understand all the details. The example is followed by some brief programming notes
which include references to other relevant sections of this document which can be consulted if
necessary. If you are interested, a more sophisticated “add” application with extra commentary can
also be found in §A.7.
SUBROUTINE ADD( STATUS ) 
INCLUDE ’SAE_PAR’ 
INTEGER STATUS, EL, NDF1, NDF2, NDF3, PNTR1( 1 ), PNTR2( 1 ), PNTR3( 1 )
* Check inherited global status and begin an NDF context.
IF ( STATUS .NE. SAI__OK ) RETURN 
CALL NDF_BEGIN 
* Obtain identifiers for the two input NDFs and trim their pixel-index
* bounds to match.
CALL NDF_ASSOC( ’IN1’, ’READ’, NDF1, STATUS ) 
CALL NDF_ASSOC( ’IN2’, ’READ’, NDF2, STATUS )
CALL NDF_MBND( ’TRIM’, NDF1, NDF2, STATUS ) 
* Create a new output NDF based on the first input NDF.
CALL NDF_PROP( NDF1, ’Axis,Quality’, ’OUT’, NDF3, STATUS ) 
* Map the input and output data arrays.
CALL NDF_MAP( NDF1, ’Data’, ’_REAL’, ’READ’, PNTR1, EL, STATUS ) 
CALL NDF_MAP( NDF2, ’Data’, ’_REAL’, ’READ’, PNTR2, EL, STATUS )
CALL NDF_MAP( NDF3, ’Data’, ’_REAL’, ’WRITE’, PNTR3, EL, STATUS )
* Check that the input arrays do not contain bad pixels.
CALL NDF_MBAD( .FALSE., NDF1, NDF2, ’Data’, .TRUE., BAD, STATUS ) 
* Add the data arrays.
CALL ADDIT( EL, %VAL( PNTR1( 1 ) ), %VAL( PNTR2( 1 ) ), 
: %VAL( PNTR3( 1 ) ), STATUS )
* End the NDF context.
CALL NDF_END( STATUS ) 
* Subroutine to perform the addition.
SUBROUTINE ADDIT( EL, A, B, C, STATUS ) 
INTEGER EL, STATUS, I
REAL A( EL ), B( EL ), C( EL )
IF ( STATUS .NE. SAI__OK ) RETURN
DO 1 I = 1, EL
C( I ) = A( I ) + B( I )
- Note that the application is actually a subroutine, called ADD, with a single integer
argument called STATUS. This is the ADAM method of writing applications (see
- The INCLUDE statement is used to define standard “symbolic constants”, such as the
value SAI__OK which is used in this routine. Such constants should always be defined in
this way rather than by using actual numerical values. The file SAE_PAR is almost always
needed, and should be included as standard in every application.
- The value of the STATUS argument is checked. This is because the application uses the
error handling strategy described in SUN/104, which requires that a subroutine should
do nothing unless its STATUS argument is set to the value SAI__OK on entry. Here, we
simply return without action if STATUS has the wrong value.
- An NDF context is now opened, by calling NDF_BEGIN. This call matches the
corresponding NDF_END call at the end of the ADD routine. When the NDF_END call
is reached, the NDF_ system will “clean up” by closing down everything which has
been used since the matching call to NDF_BEGIN (see §3.4). Since we want to clean up
everything in the application at this point, the initial call to NDF_BEGIN is put right at
- The two input NDFs which we want to add are now obtained using the parameters
‘IN1’ and ‘IN2’. Lots of things happen behind the scenes at this point, possibly involving
prompting the user to supply the names of the data structures to be added, and a pair
of integer values NDF1 and NDF2 are returned. These values are NDF identifiers and are
used to refer to the NDFs throughout the rest of the application (see §3.2).
- The first thing we do with these identifiers is to pass them to NDF_MBND. This routine
ensures that the two NDFs are the same shape and size, which is what we require. The
details of how this is done are explained much later (in §17.3). For now, just accept that it
- An output NDF is created next by calling NDF_PROP which uses the parameter ‘OUT’
to get the new data structure (probably prompting the user for its name) and returns
another identifier for it in NDF3. NDF_PROP bases the new NDF on the first input NDF
(see §14.4). This ensures that it’s the right size, etc., and also that it contains any ancillary
information which can legitimately be copied from the input.
- The data arrays in the input and output NDFs are then accessed by calling NDF_MAP.
Rather than returning actual data values, this routine returns pointers for the data values
in PNTR1, PNTR2 and PNTR3 (see §8.2). Note that we want to ‘READ’ the input arrays
and ‘WRITE’ to the output array.
- Since this is a very simple application, it cannot handle the special bad-pixel values which
may be present in some NDF data structures. The call to NDF_MBAD at this point checks
that there are none present (see §17.2). If there are, then an appropriate error message will
result and the application will abort.
- The subroutine ADDIT which performs the work is now called. The pointer values
returned by NDF_MAP are turned into actual Fortran arrays at this point, which ADDIT
can access. This is done by using the %VAL function in the call to ADDIT (see §8.2).
- ADDIT itself is a very simple subroutine. Since all the arrays it will be passed are the
same size (we have ensured this), there is no need to worry about their dimensions. They
are all handled as if they were 1-dimensional, and simply added. The application could
easily be altered to perform a different function by changing this routine.
- Finally, NDF_END is called. As already explained, this shuts everything down, ensuring
that all NDF data files are closed, etc. before the application finishes.
Routines for Accessing the
Extensible N-Dimensional Data Format
- TOC ↑