### 4 A simple example application

Example is always more efficacious than precept.
Lives of the English Poets vol. ii
Samuel Johnson

This section will walk you through the Fortran source code for a simple application which uses the CAT library to write a small catalogue. First, however, a few preliminaries.

• The programming interface to the CAT library comprises two components: the CAT subroutines and two INCLUDE files, CAT_PAR and CAT_ERR. These INCLUDE files define symbolic constants which application programs may use. Section 3.2 explains how to access these files. It is worth your while printing out and examining copies of both files. The comments included in the files should be sufficient to explain the purpose of each constant. CAT_PAR contains general constants pertaining to the CAT library. CAT_ERR contains constants corresponding to the various error codes which can be set by the CAT library. If your application needs to access one of these values you should always use the appropriate symbolic constant; never hard-code the actual value into your code. The values may (and probably will) change in subsequent releases of the CAT library.
• Within the CAT interface items in a catalogue, such as columns and parameters, and, indeed, the catalogue itself, are identified by an identifier. This approach is consistent with the treatment of data items in other ADAM libraries. Each identifier is an INTEGER number. The value of an identifier is unique (within a given invocation of an application) and is sufficient to identify the item to which it refers. You should observe the following simple rules when using identifiers in applications.
• You should never set the value of a new identifier yourself; CAT will always generate an identifier for you.
• You should never alter the value of an identifier once CAT has allocated it.
• You never need to know the actual value of an identifier. I suppose that you can print them out, if you really want to (they are just numbers), but the information is of no use to you.
• Null values for fields are indicated by a separate flag indicating whether the associated datum is null or not. This flag is of type LOGICAL and is coded as follows:
.TRUE.
– the field is null; no datum is available,
.FALSE.
– the field is not null; a valid datum is available.

This scheme is adopted in both the routines to get a value from a catalogue and those to put a value to a field in a catalogue. It is adopted to avoid any possible ambiguity in interpreting null values.

When a null value is obtained from a catalogue the actual datum returned will be the ADAM ‘bad’ value for the appropriate data type (where one is available). This procedure facilitates passing values obtained from CAT into other ADAM subroutines. It means, however, that the null value returned through the CAT interface is not necessarily the same as the representation of the null stored in the catalogue. Existing catalogues, for example FITS tables from external sources, can come with a variety of values used to represent null values. The necessary checks and substitutions are performed automatically and invisibly within the catalogue-format specific parts of the CAT library. Your application will simply see the appropriate ADAM ‘bad’ value.

Not all catalogues support null values in all their columns. If a column does not support null values then the null value flag will always be returned set to .FALSE.

The treatment of null values is described in greater detail in Section 8.2. In particular, Section 8.2.5 prescribes how applications should handle null values.

We are now ready to examine a simple application which uses the CAT library. We will use one of the example applications released with the library, EXAMPLE_WRITE. This example creates and writes values to a small catalogue. I strongly recommend that you print out a copy of the source code and refer to it as you work through the example. The source code is available in file:

/star/share/cat/example_write.f

This example program is simpler than a real application. Starting at the beginning, the first thing to notice about the application is that, because it is an ADAM A-task, at its top level it is a subroutine, not a main program:

SUBROUTINE EXAMPLE_WRITE (STATUS)

The status argument is used to keep track of the success of the application as it proceeds with its task.

*+
*  Name:
*     EXAMPLE_WRITE
.
.
.

After the ADAM prologue comments the CAT symbolic constants are INCLUDEd. Some of the constants defined in this file will be used by the application.

*  Global Constants:
INCLUDE ’SAE_PAR’
INCLUDE ’CAT_PAR’

The various variables used in the application are defined next.

*  Status:
INTEGER STATUS             ! Global status.
*  Local Variables:
INTEGER
:  CI,       ! Catalogue identifier.
:  QII,      ! Identifier for a real parameter.
:  QIR,      ! Identifier for a real parameter.
:  QIC,      ! Identifier for a character parameter .
:  FII,      ! Identifier for an integer column (or field).
:  FIR,      ! Identifier for a real column (or field).
:  FIC,      ! Identifier for a character column (or field).
:  LOOP      ! Loop index.
INTEGER
:  VALI      ! Integer value.
REAL
:  VALR      ! Real value.
CHARACTER
:  VALC*10   ! Character value.
LOGICAL
:  NULI,     ! Null flag corresponding to VALI.
:  NULR,     !  "    "         "       "  VALR.
:  NULC      !  "    "         "       "  VALC.
*.

The first executable statement is the usual check that the status is ok. This check is mandatory in ADAM applications.

IF (STATUS .EQ. SAI__OK) THEN

Once it is out of the way the application proper can start. It starts with a call to CAT_CREAT:

CALL CAT_CREAT (’CNAME’, CI, STATUS)

This subroutine will create a new catalogue, whose name it obtains from the ADAM parameter system (in practice the user will be prompted for it)3. The first argument of CAT_CREAT is the name of the ADAM parameter which will supply the catalogue name. The remaining two arguments are returned by CAT_CREAT; CI is the identifier for the catalogue. We will use this variable to refer to the catalogue throughout the application. STATUS is the running status argument which is usual in ADAM libraries. If the routine has succeeded its value is SAI__OK.

After CAT_CREAT has executed successfully a new catalogue has been created, but no columns or parameters have been defined for it, and it contains no data. The next step is to define some columns. The first column defined is an INTEGER column called COLI. Subroutine CAT_PNEW0 is used for this task:

CALL CAT_PNEW0 (CI, CAT__FITYP, ’COLI’, CAT__TYPEI, FII,
:     STATUS)

The first argument, CI, identifies the catalogue to which the new column belongs. The second argument tells CAT_PNEW0 that it has to create a new column; this argument should always be CAT__FITYP when a column is to be created. The third argument is the name of the column, COLI in the present case, and the fourth argument defines its data type. There are symbolic constants defined in CAT_PAR for each of the data types supported by CAT. COLI is an INTEGER column, so the code for an INTEGER is used. The codes for the different data types are listed in Table 4.

The fifth argument, FII, is returned rather than given and is an identifier for the column; subsequent references to the column will be via this identifier. The final argument is the usual ADAM running status.

The mandatory information which you must supply to define a column is: the catalogue to which it belongs, its name and its data type. All these values are specified using routine CAT_PNEW0. However, as explained in Section 2, a column is defined by a set of attributes, of which the name and data type are but two, albeit mandatory ones. Section 6.7 lists all the attributes for a column. If you do not specify the remaining attributes default values are adopted for them. You can, however, supply your own values to over-ride the defaults. The next line is an example of doing so.

CALL CAT_TATTC (FII, ’COMM’, ’Integer column’, STATUS)

This routine is one of a family of similar routines, one for each data type (CAT_TATTC for attributes of data type CHARACTER, CAT_TATTI attributes of type INTEGER etc). Here it is used to set the comment attribute, COMM of column FII to the value ‘Integer column’. Other attributes of column COLI could have been set in the same way, but in this example they are not, and the defaults are adopted4.

Two additional columns are created in the same way; the REAL column COLR and the CHARACTER column COLC. Note that in the case of COLC the CHARACTER size attribute CSIZE is set using routine CAT_TATTI.

CALL CAT_PNEW0 (CI, CAT__FITYP, ’COLR’, CAT__TYPER, FIR,
:     STATUS)
CALL CAT_TATTC (FIR, ’COMM’, ’Real column’, STATUS)

CALL CAT_PNEW0 (CI, CAT__FITYP, ’COLC’, CAT__TYPEC, FIC,
:     STATUS)
CALL CAT_TATTC (FIC, ’COMM’, ’Character column’, STATUS)
CALL CAT_TATTI (FIC, ’CSIZE’, 10, STATUS)

An important restriction to remember is that columns must be created, and their attributes set, before any rows of data are written to the catalogue. Once a table of values have been written to a catalogue the details of its existing columns are frozen and no new columns can be created for it.

After creating the columns, some parameters are created. The first of these is the INTEGER parameter PARI. It is created with the same routine that was used to create the columns, CAT_PNEW0:

CALL CAT_PNEW0 (CI, CAT__QITYP, ’PARI’, CAT__TYPEI, QII,
:     STATUS)

Again the first argument is the catalogue identifier. The second argument indicates that a parameter is to be created; this argument should always be set to CAT__QITYP when a parameter is to be created. The remaining arguments are exactly the same as they were for creating a column: PARI is the parameter name, CAT__TYPEI the data type and QII and STATUS are the identifier for the parameter and the running status, respectively.

As for columns, only the minimum, mandatory attributes for the parameter are set with CAT_PNEW0, and default values are adopted for the remaining attributes. Section 6.9 lists the attributes of a parameter. Also just like columns, the attributes of parameters can be set using the CAT_TATT$<$t$>$ family of routines (where t = C for CHARACTER attributes, I for INTEGER etc). In the example CAT_TATTC is used to set the comments attribute, COMM of parameter PARI and CAT_TATTI is used to set the value attribute VALUE. The latter point is worth remembering; having created a parameter with CAT_PNEW0 it is necessary to use one of the CAT_TATT$<$t$>$ routines to set its value, and the routine chosen should correspond to the data type of the parameter5.

CALL CAT_TATTC (QII, ’COMM’, ’Integer parameter’, STATUS)
CALL CAT_TATTI (QII, ’VALUE’, 23, STATUS)

Two additional parameters are created; the REAL parameter PARR and the CHARACTER parameter PARC. Note that, as was the case for columns, for the CHARACTER parameter PARC the CHARACTER size attribute ‘CSIZE’ must be set using CAT_TATTI.

CALL CAT_PNEW0 (CI, CAT__QITYP, ’PARR’, CAT__TYPER, QIR,
:     STATUS)
CALL CAT_TATTC (QIR, ’COMM’, ’Real parameter’, STATUS)
CALL CAT_TATTR (QIR, ’VALUE’, 42.0, STATUS)

CALL CAT_PNEW0 (CI, CAT__QITYP, ’PARC’, CAT__TYPEC, QIC,
:     STATUS)
CALL CAT_TATTC (QIC, ’COMM’, ’Character parameter’, STATUS)
CALL CAT_TATTI (QIC, ’CSIZE’, 20, STATUS)
CALL CAT_TATTC (QIC, ’VALUE’, ’Example string’, STATUS)

Note that, unlike columns, parameters can be created at any stage while writing a catalogue. They do not have to be created prior to writing the table of values for the catalogue.

Once the parameters have been created the example starts a loop which will write the table of values.

DO LOOP = 1, 25

In the CAT interface the basic method of writing (and reading) a catalogue is one row at a time. Each increment of the loop will correspond to a separate row of the catalogue, and thus in total twenty-five rows will be written, numbered from one to twenty-five.

The first few lines of code inside the loop are concerned with inventing values to write to the catalogue. The next few lines set the null value flags (all the rows contain non-null values, except row ten, where the flags are set to null). In a real application, of course, these values would not be invented, but would either be the results of a calculation or read from an external file.

VALI = LOOP
VALR = 2.3E1 + REAL(LOOP)
VALC = ’ ’
WRITE(VALC, 4000) LOOP
4000       FORMAT(’ Loop ’,I3, ’%’)

NULI = .FALSE.
NULR = .FALSE.
NULC = .FALSE.

*
*          Make all the columns contain null values for row 10.

IF (LOOP .EQ. 10) THEN
NULI = .TRUE.
NULR = .TRUE.
NULC = .TRUE.
END IF

The line:

CALL CAT_PUT0I (FII, VALI, NULI, STATUS)

writes a single value to column COLI. FII is the column identifier, VALI and NULI the value and null value flag respectively. STATUS is, of course, the running status. CAT_PUT0I is one of the family of CAT_PUT0$<$t$>$ routines, with one routine per data type. (I for INTEGER, C for CHARACTER etc). Thus there are corresponding calls to write the REAL and CHARACTER columns COLR and COLC:

CALL CAT_PUT0R (FIR, VALR, NULR, STATUS)
CALL CAT_PUT0C (FIC, VALC, NULC, STATUS)

CAT has the concept of the ‘current row buffer’, which is an internal copy of the row of the catalogue which it is working on currently. The CAT_PUT0$<$t$>$ routines put fields to the current row buffer (and the corresponding CAT_GET0$<$t$>$ routines read values from it).

CALL CAT_RAPND (CI, STATUS)

Writes out the current row buffer for catalogue CI to the actual catalogue file, appending it to the end of the catalogue. The internal current row buffer is initialized ready to receive new values, and the internal count of the actual catalogue row number to which the current row buffer corresponds is incremented by one. That is, CAT_RAPND takes care of writing the current row buffer to the catalogue and readies CAT to receive values for a new current row buffer. When a catalogue is created the current row buffer is initialized automatically and the row number to which it corresponds is set to one.

Thus a loop with one increment corresponding to one row in the catalogue and containing calls to CAT_PUT0$<$t$>$ routines to put values to the current row buffer and a call to CAT_RAPND to append the current row buffer to the catalogue is all that is necessary to write a table of values to a catalogue. The current row has been written, so the loop generating each row can now terminate:

END DO

There is one final call to a CAT routine:

CALL CAT_TRLSE (CI, STATUS)

This call ‘releases’ catalogue identifier CI and closes the corresponding catalogue. Identifiers to items within the catalogue, such as the various columns and parameters, are also released. Any remaining values are written to disk, the files are closed etc. The creation of the catalogue is complete. Finally, if all has succeeded, the application reports a message and then terminates.

IF (STATUS .EQ. SAI__OK) THEN
CALL MSG_OUT (’ ’, ’Catalogue created successfully.’,
:        STATUS)
ELSE
CALL ERR_REP (’EXAMPLE_WRITE_ERR’, ’Failed to create ’/
:        /’catalogue.’, STATUS)
END IF

END IF

END

Having worked through this example application you might like to try it out. Type:

example_write

You will be prompted for the catalogue name. Reply, for example:

test.fit

After a couple of moments the message ‘Catalogue created successfully.’ should appear and a binary FITS table called test.fit should have been created in your current directory. You can examine its contents using the xcatview catalogue browser in CURSA (see SUN/190[3]) or the listout utility in CAT-EXAMPLES. For the latter simply type:

listout

and answer the prompts (see Section 3.4 for details).

Another example program is available which reads back the catalogue created by EXAMPLE_WRITE. It is called EXAMPLE_READ and the source code is available in file:

You might like to print out a copy and try to understand it. If you have followed the discussion for EXAMPLE_WRITE you should be able to do so without any difficulty.
3It is possible to create or open a CAT catalogue without going through the ADAM parameter system by calling routine CAT_TOPEN rather than CAT_CREAT. This routine is not used in the present example, but is described in Section 7.5, below.
4For convenience two additional routines, which are not used in the present example, are provided for creating columns. CAT_CNEWS creates a column and simultaneously sets some of the more commonly used attributes; CAT_CNEWA creates a column and simultaneously sets all of its attributes. These routines may be more convenient to use than a call to CAT_PNEW0 followed by multiple calls to CAT_TATT$<$t$>$. They are described in Section 7.8.3, below.
5For convenience two additional routines, which are not used in the present example, are provided for creating parameters. CAT_PPTS$<$t$>$ creates a parameter and simultaneously sets some of the more commonly used attributes; CAT_PPTA$<$t$>$ creates a parameter and simultaneously sets all of its attributes. These routines may be more convenient to use than a call to CAT_PNEW0 followed by multiple calls to CAT_TATT$<$t$>$. They are described in Section 7.8.4, below.