### 20 CONNECTING WITH THE DATA SYSTEM

#### 20.1 NDF Names

So far, examples of obtaining access to NDF data structures have depended on routines which use parameters and therefore require the support of a programming environment. However, it is also possible to use NDF_ routines in “standalone” applications and hence to access NDF structures whose location within the underlying data system (HDS) is specified explicitly, i.e. by name.

The name of an NDF dataset normally consists of up to three parts, as follows:20

<file_name><hds_path><subscripts>

Here, $<$file_name$>$ is the name of the HDS “container file” which holds the data, $<$hds_path$>$ is the HDS “path” which identifies the location of the HDS object (i.e. the NDF) within the container file, and $<$subscripts$>$ is a parenthesised set of subscripts which may be used to select a section from the NDF (see §16). The container file name is always required, but the other two components are optional. Thus, a simple NDF name might look like:

mydatafile

(if the NDF were the top-level object in the container file, which is often the case), while a fairly complicated NDF name might resemble:

/users/bill/datafiles/today.RUN(66).BAND_B(1:66,1.04)

Here, the first ‘.’ separates the HDS path from the file name. Names of this sort may be supplied by users of NDF_ applications in response to prompts issued via a programming environment.

However, when NDF names are specified explicitly within applications, an alternative naming possibility exists because HDS files contain hierarchies of data structures rather similar to the directory structure of computer filing systems (see SUN/92 for a description of HDS data structures). Therefore, just as you can specify a file using an absolute name or a name relative to the current directory, so you can also specify the location of an NDF data structure within an HDS container file in an absolute or a relative fashion.

To give an absolute NDF name, you would specify it in full, as in the examples above,21 while to give a relative NDF name, you would supply an active HDS locator and the name of the NDF data structure relative to the HDS object that the locator identifies.

To allow this, most NDF_ routines that handle the names of NDFs will accept two arguments: an HDS locator and an associated character string containing a name. The following section illustrates how these are used.

#### 20.2 Finding and Importing NDFs

Using the example above, suppose that LOC is an active HDS locator associated with the data structure:

/users/bill/datafiles/today.RUN(66)

The routine NDF_FIND could then be used to find an NDF data structure relative to this object, and to import it into (i.e. make it known to) the NDF_ library, as follows:

CALL NDF_FIND( LOC, ’BAND_B(1:66,1.04)’, INDF, STATUS )

(note that here we have specified a relative name for the second argument). This would find the NDF section whose absolute name is:

/users/bill/datafiles/today.RUN(66).BAND_B(1:66,1.04)

An identifier for this NDF would be returned via the INDF argument and the data structure could then be manipulated using other NDF_ routines. The locator passed to NDF_FIND may be annulled afterwards without affecting the subsequent behaviour of the NDF_ system.

Note that the relative name supplied could be any trailing fragment of the full (absolute) name, and might consist simply of a subscript – so long as a suitable locator was also available. In fact, the relative name could be entirely blank, in which case the locator supplied would be taken to identify the NDF directly. Thus, in:

CALL NDF_FIND( LOC, ’ ’, INDF, STATUS )

the locator LOC should be associated with the NDF data structure itself.

To specify an NDF in a call to NDF_FIND using an absolute name instead, we utilise the special locator value DAT__ROOT, which is provided by HDS and defined in the include file DAT_PAR. This locator represents the notional HDS object which is the parent of all other HDS objects,22 and specifying it as a locator value allows the associated NDF name to be an absolute name. Thus:

INCLUDE ’DAT_PAR’

...

CALL NDF_FIND( DAT__ROOT,
:               ’/users/bill/datafiles/today.RUN(66).BAND_B(1:66,1.04)’,
:               INDF, STATUS )

would access the same NDF section as in the examples above, this time using its absolute name.

#### 20.3 A Note on Modes of Access

When obtaining an NDF identifier for a data structure using NDF_FIND, the NDF library has to decide what mode of access to grant (i.e. whether it should allow the NDF to be modified or whether it should be “read only”). It normally does this by inspecting the locator supplied and permitting modification of the NDF only if the locator itself permitted modification of the data structure. Access can subsequently be further restricted, if required, using NDF_NOACC (see §23.1).

However, if the locator supplied has the value DAT__ROOT (indicating that an absolute NDF name has been given), then this method cannot be applied. In this case, read only access will be obtained by NDF_FIND and subsequent modification of the NDF will not be permitted. If the ability to modify the NDF is required, the routine NDF_OPEN should be used instead, as this allows the access mode to be specified (see §20.10).

#### 20.4 Obtaining an HDS Locator for an NDF

The NDF_ system also allows you to obtain an HDS locator for a data structure whose NDF identifier you supply. In effect, this is the reverse of the importation process described in §20.2. It is performed by the NDF_LOC routine, as follows:

CALL NDF_LOC( INDF, MODE, LOC, STATUS )

The MODE argument specifies the mode of access required (‘READ’, ‘UPDATE’ or ‘WRITE’) and LOC returns the resulting HDS locator. Note that you should annul this locator (e.g. using DAT_ANNUL) when it is no longer required, as the NDF_ system will not perform this task itself.

#### 20.5 NDF Placeholders

Routines are also provided for the creation of NDFs at specified locations within the data system, rather than indirectly via parameters. These routines depend on the concept of a placeholder for their operation.

In many ways, a placeholder is similar to an NDF identifier; i.e. it is an integer value which the NDF_ system issues to identify an entity about which it holds information. In this case, however, the entity is not a data structure, but a position within the data system at which an NDF will be created at some later time.

An NDF placeholder may be obtained by calling the routine NDF_PLACE and specifying a name for the new NDF using an HDS locator and an absolute or relative name in the same way as when accessing an existing NDF (§20.2). Thus, if LOC is a locator associated with an existing HDS structure, then:

CALL NDF_PLACE( LOC, ’NEW_NDF’, PLACE, STATUS )

would return an integer value PLACE, which is a placeholder for a new NDF called ’NEW_NDF’ contained within that structure. Similarly, the absolute name of a new NDF could be specified as follows:

INCLUDE ’DAT_PAR’

...

CALL NDF_PLACE( DAT__ROOT, ’file.STRUCT(2,2).NEW_NDF’, PLACE, STATUS )

All the HDS structures residing at levels above the actual NDF object to be created must already exist, otherwise NDF_PLACE will fail.

Sometimes, it is more convenient if an object is already in existence at the location in the data system for which a placeholder is to be issued. For instance, this allows placeholders to refer to the individual elements of an array of structures which has previously been created, so that arrays of NDFs may be built. Any pre-existing object for which an NDF placeholder is to be issued must be a scalar structure and must have an HDS type of ‘NDF’. It must also be empty (i.e. it must have no components). NDF_PLACE is then called in the usual way. In the following example, for instance, a 1-dimensional array of structures is created with 5 elements, and a placeholder is then obtained for the second element:

INTEGER DIM( 1 )

...

DIM( 1 ) = 5
CALL DAT_NEW( LOC, ’NDF_ARRAY’, ’NDF’, 1, DIM, STATUS )
CALL NDF_PLACE( LOC, ’NDF_ARRAY(2)’, PLACE, STATUS )

As with NDF_FIND, a blank name string may be supplied to NDF_PLACE to indicate that the data system location to be used is identified directly by the locator; this mode of use is only applicable when an object already exists at that location.

#### 20.6 Creating NDFs via Placeholders

The only valid thing that can be done with an NDF placeholder is to pass it to a routine which creates an NDF. Thus, to create a new simple NDF by this means, the routine NDF_NEW might be used along with NDF_PLACE, as follows:

CALL NDF_PLACE( LOC, ’NEW_NDF’, PLACE, STATUS )
CALL NDF_NEW( ’_INTEGER’, NDIM, LBND, UBND, PLACE, INDF, STATUS )

The call to NDF_NEW will create the required new simple NDF (in this case with a type of ‘_INTEGER’) in place of the placeholder PLACE; i.e. in component NEW_NDF of the HDS structure with locator LOC. This act of creation automatically annuls the placeholder, whose value is reset to NDF__NOPL (this value is defined in the include file NDF_PAR and is reserved for indicating that a variable is not a valid placeholder).

A similar routine NDF_NEWP also exists by analogy with NDF_CREP (see §13.4) to create a primitive NDF via a placeholder in exactly the same way.

Note that placeholders are only intended for local use within an application and only a limited number of them are available simultaneously. They are always annulled as soon as they are passed to a routine which creates an NDF, where they are effectively exchanged for an NDF identifier. Since this is the only valid fate for a placeholder, there is no routine for annulling them explicitly. However, the routine NDF_END will annul any which are left outstanding as part of the “cleaning up” role it performs at the end of an NDF context (see §3.4).

#### 20.7 Temporary NDFs

Placeholders also provide a convenient means of obtaining “scratch” NDFs for temporary use within an application. In this case, exactly the same NDF creation routine can be used, but the placeholder is obtained using the routine NDF_TEMP, which returns a placeholder for a temporary NDF. To create a temporary simple NDF, the following calls might be used:

CALL NDF_TEMP( PLACE, STATUS )
CALL NDF_NEW( ’_INTEGER’, NDIM, LBND, UBND, PLACE, INDF, STATUS )

The resulting NDF identifier INDF may be used in exactly the same way as an identifier for a permanent NDF, except that the data structure it is associated with will not be retained after it has been finished with. More specifically, a temporary NDF will be deleted as soon as the last NDF identifier associated with it is annulled, either explicitly (i.e. with NDF_ANNUL) or implicitly (e.g. as part of the cleaning up performed by NDF_END).

It is possible to determine whether an existing NDF identifier refers to a temporary NDF using the routine NDF_ISTMP:

CALL NDF_ISTMP( INDF, ISTMP, STATUS )

A logical value of .TRUE. is returned via the ISTMP argument if the NDF is temporary.

#### 20.8 Copying NDFs

Placeholders also play a role in the copying of NDF data structures from one location to another within the data system. In this case they are used to identify the destination for the copying operation, which is performed by the routine NDF_COPY. This should be preceded by a call which generates a suitable placeholder. For instance:

CALL NDF_PLACE( LOC, ’MY_NDF_COPY’, PLACE, STATUS )
CALL NDF_COPY( INDF1, PLACE, INDF2, STATUS )

Here, the placeholder causes NDF_COPY to copy the NDF with identifier INDF1 to form a new NDF in the component MY_NDF_COPY of the HDS structure with locator LOC. As usual, the placeholder is annulled by the copying operation and a new NDF identifier INDF2 is issued to refer to the new data structure. This new structure contains all the information which was present in the original. It is possible to copy both base NDFs and NDF sections in this way, but the copying operation always creates a new base NDF.

Naturally, it is also possible to create a temporary copy of an NDF by using NDF_TEMP to obtain the placeholder, as follows:

CALL NDF_TEMP( PLACE, STATUS )
CALL NDF_COPY( INDF1, PLACE, INDF2, STATUS )

#### 20.9 Selective Copying of NDF Components

Copying of selected NDF components is also possible by using the routine NDF_SCOPY. This behaves in the same way as NDF_COPY, except that it also accepts a list of the NDF components to be copied as its second argument, thus:

CALL NDF_SCOPY( INDF1, ’Data,Var,Axis,Nohist,Noext(CCDPACK)’,
:                PLACE, INDF2, STATUS )

The component list has the same syntax, and is used in exactly the same way, as in the routine NDF_PROP (see §§14.514.8). In fact, NDF_SCOPY is the “standalone” equivalent of NDF_PROP and similar rules about the propagation of NDF component information should be observed when using it (see §14).

The routine NDF_OPEN is a general-purpose routine for accessing NDF data structures. It will perform the same tasks as NDF_FIND and NDF_PLACE but, although it is a little more complicated, it also offers some additional features. In particular, using NDF_OPEN to access an NDF is similar to using a Fortran OPEN statement to access a file, in that it allows you to specify whether you think the NDF already exists or not. It also allows you to specify an access mode.

If the fourth (STAT) argument to NDF_OPEN is set to ’OLD’, the routine behaves like NDF_FIND, allowing you to access an existing NDF. For example:

CALL NDF_OPEN( DAT__ROOT, ’any_ndf’, ’UPDATE’, ’OLD’, INDF, PLACE, STATUS )

would obtain ‘UPDATE’ access to the specified NDF and return an identifier for it via the INDF argument (in this case the PLACE argument is not used and returns the value NDF__NOPL). An error would result if the NDF did not exist.

If the fourth argument is set to ’NEW’, NDF_OPEN instead behaves like NDF_PLACE. A placeholder for the specified NDF is returned via the PLACE argument, and the INDF argument (which is not used in this case) returns the value NDF__NOID.

Finally, if its fourth argument is set to ’UNKNOWN’, then NDF_OPEN first attempts to access an existing NDF data structure using the locator and name supplied. If it succeeds, an identifier for the NDF is returned via the INDF argument. However, if it fails because the NDF does not exist, it instead creates a new placeholder for it and returns this via the PLACE argument. You can tell which of these two possibilities occurred by examining the INDF and PLACE arguments on return – only one of these will differ from its “null” value (NDF__NOID or NDF__NOPL respectively).

A typical use of NDF_OPEN might be to access an existing NDF if it exists, or to create a new one if it does not, as follows:

CALL NDF_OPEN( LOC, ’CALIB.BAND_B’, ’UPDATE’, ’UNKNOWN’,
:               INDF, PLACE,  STATUS )
IF ( PLACE .NE. NDF__NOPL ) THEN
CALL NDF_NEWP( ’_INTEGER’, 1, UBND, PLACE, INDF, STATUS )
END IF

#### 20.11 Deleting NDFs

Deletion of an NDF may be performed by calling the routine NDF_DELET, thus:

CALL NDF_DELET( INDF, STATUS )

If the necessary access is available (see §23.1), the associated data structure will be erased and a value of NDF__NOID will be returned for the INDF argument. Any other identifiers associated with the same data structure will also become invalid at this point. Note that deletion cannot be performed via an identifier which refers to an NDF section. In this case, NDF_DELET will simply annul the identifier as if NDF_ANNUL had been called (see §3.3).

20Different rules may apply when accessing foreign format datasets and these are described in SSN/20.

21Note that an absolute NDF name may nevertheless still start with a container file specified using a relative file name.

22In essence it stands for the filing system of the host computer.