5 Writing a Primitive

 5.1 Log Files
 5.2 Temporary and intermediate files
 5.3 Passing information between primitives

In order to write a valid primitive certain steps must be adhered to in order to ensure that subsequent primitives have the correct information.

It assumes knowledge of the following Perl concepts: using Perl objects, lexical variables, Perl data structures.

Here is an example primitive showing the basic principles:

   1  =head1 NAME
   2
   3  _PRIMITIVE_NAME_ - short description
   4
   5  =head1 DESCRIPTION
   6
   7  Long description
   8
   9  =head1 ARGUMENTS
  10
  11  =over 4
  12
  13  =item ARG1
  14
  15  Description of possible values of ARG1
  16
  17  etc...
  18
  19  =back
  20
  21  =head1 TASKS
  22
  23  List of external tasks required by the primitive
  24
  25  =head1 OUTPUT FILES
  26
  27  Output suffix for the display system.
  28
  29  etc, AUTHORS, COPYRIGHT.....
  30
  31  =cut
  32
  33  # Read arguments or use default values
  34  my $arg1 = ( exists $_PRIMITIVE_NAME{ARG1} ?
  35               $_PRIMITIVE_NAME{ARG1} : 5);
  36
  37  # Loop over all sub frames
  38  foreach my $i (1..$Frm->nfiles) {
  39
  40    # Get the input and output filename
  41    my ($in, $out) = $Frm->inout(’_sfx’, $i);
  42
  43    # Read some value from the frame FITS header
  44    my $value = $Frm->hdr(’KEYWORD’);
  45
  46    # Combine the in, out and value into options for the task
  47    # DEPENDS ON ALGORITHM ENGINE
  48    my $options = "IN=$in OUT=$out SWITCH=$value";
  49
  50    # Run the algorithm engine
  51    $Mon{’task’}->obeyw(’TASK’,$options);
  52
  53    # Retrieve an answer from a parameter
  54    ($ORAC_STATUS, $result) = $Mon{’task’}->get(’TASK’,’PARAMETER’);
  55
  56    # Print the result
  57    orac_print "Result from primitive $ORAC_PRIMITIVE = $result\n";
  58
  59    # Update the frame object so that the next primitive
  60    # gets the correct input file name
  61    $Frm->file($i, $out);
  62
  63  }
  64
  65  # Ask the display system to display the frame
  66  $Display->display_data($Frm) if defined $Display
  67

The following should be noted:

5.1 Log Files

It is sometimes desirable to write results to log files as data files are processed (for example, seeing statistics, pointing offsets etc). Rather than force the primitive writer to check for the existence of log files and decide whether or not to open or append to log files, the orac-dr system provides a simplified access to log file creation via the ORAC:LogFile class.

All that is required to write an entry to a log file is for the following methods to be invoked:

  my $log = new ORAC::LogFile(’log.whatever’);
  $log->header(@header);
  $log->addentry(@lines);

The header will only be written to the log file if the log file does not previously exist so it is safe to run this command in a primitive without an explicit check. Both the header() and addentry() methods accept arrays, and newline characters will be appended to each item in the array when written to the log file. The convention is that all log file names should start with ‘log.’.

5.2 Temporary and intermediate files

In many cases, it is necessary to make use of temporary files within a primitive, either for intermediate data steps that are not relevant for the frame, or as text files input to external tasks. Since these are not required once the primitive is finished a class is provided for dealing with temporary files (ORAC::TempFile).

This class will choose a filename and, optionally, open the file ready for read-write access (when this facility is used it is guaranteed that the file is unique and will not overwrite any existing file). The file, and any files of the same name but with a .sdf extension6, are removed when the variable goes out of scope.

The only files that should remain when a primitive completes should be those registered with the current frame or the current group. All others should be temporary and should be tidied up on leaving the primitive (which is automatic if ORAC::TempFile is used)7. This allows the final tidyup primitive to be responsible solely for removing unwanted intermediate frames that were the product of individual primitives (every time a the file name is updated in a frame object the previous value is stored for possible later removal by the tidy primitive).

5.3 Passing information between primitives

Since each primitive is evaluated in its own scope, it is not possible (or even desirable) to pass simple variables between separate primitives. Two means are provided for doing this:

The first method using the primitive hash only allows information to be passed within the current recipe whereas the frame header allows the information to be retained for subsequent frame processing.

5Although it is not possible for a user to specify a primitive argument value of undef this is still good programming practice.

6the extension used for Starlink N-Dimensional data format (NDF)

7This is not always a good idea when debugging. Future versions of the pipeline will disable the removal of temporary files when the -debug flag is in use

8It will not simply return undef. The recipe will fail to run since the hash would not have been declared previously.