6 Data manipulation

This next example adds 7.0 to all the values in the main data array of the input NDF. Although not the most useful of applications in itself, it does illustrate the method usually used in ADAM programs to manipulate data arrays and introduces the concept of dynamic memory mapping.

        SUBROUTINE ADD7 (STATUS)
        IMPLICIT NONE
        INCLUDE ’SAE_PAR’
        INTEGER STATUS, NELM, NDF1, PTR1
        REAL VALUE
  
  *  Check inherited global status.
        IF (STATUS.NE.SAI__OK) RETURN
  
  *  Begin an NDF context.
        CALL NDF_BEGIN
  
  *  Obtain an identifier for the input NDF.
        CALL NDF_ASSOC (’INPUT’, ’UPDATE’, NDF1, STATUS)
  
  *  Map the NDF data array.
        CALL NDF_MAP (NDF1, ’Data’, ’_REAL’, ’UPDATE’, PTR1, NELM, STATUS)
  
  *  Assign a value of 7.0 to VALUE.
        VALUE = 7.0
  
  *  Add the constant value to the data array.
        CALL ADDIT (NELM, %VAL (PTR1), VALUE, STATUS)
  
  *  End the NDF context.
        CALL NDF_END (STATUS)
        END
  
  *  Subroutine to perform the addition.
        SUBROUTINE ADDIT (NELM, A, VALUE, STATUS)
        IMPLICIT NONE
        INCLUDE ’SAE_PAR’
        INTEGER NELM, STATUS, I
        REAL A(NELM), VALUE
  
  *   Perform the addition.
        IF (STATUS.NE.SAI__OK) RETURN
        DO I = 1, NELM
           A(I) = A(I) + VALUE
        ENDDO
        END

And the interface file:

  interface ADD7
    parameter      INPUT
       prompt      ’Input NDF structure’
    endparameter
  endinterface

Dynamic memory mapping.

In order to get access to a data array, a Fortran program might declare an array of some fixed size, and read the data values into it. The problems with this approach are that such a program will contain an array larger than necessary for most purposes, and cannot deal with an array larger than that explicitly declared.

The solution is to exploit the method that most compilers use to pass values between subroutines. When a value is passed from one program unit to another via an argument list, usually9 what is transferred is not the value itself, but simply the address of the storage location where the value is held. Thus a subroutine to which an array is passed does not have its own copy of that array, but just knows where to find it.

In the example program, ADD7, you will notice that the main subroutine does not declare an array at all. However it does have the INTEGER PTR1. The ‘map’ call below reads the data from the NDF into the computer’s memory, and returns the pointer PTR1 whose value is the actual memory address of the first byte of the allocated memory. Also returned is NELM, the number of elements in the data array.

        CALL NDF_MAP (NDF1, ’Data’, ’_REAL’, ’UPDATE’, PTR1, NELM, STATUS)

This address (PTR1) cannot be used to access the data array in the subroutine ADD7. But it can be used in the call to the subroutine ADDIT. Merely inserting PTR1 into the argument list of the call to ADDIT will not produce the desired result. This is because the subroutine will receive not the actual value of PTR1, but the address of PTR1 itself, and thus will simply have access to the integer variable PTR1 (as you would expect).

However, VAX Fortran supports a special extension called %VAL, which forces the actual value of a variable to be passed to the subroutine.10 Thus in the call below, passing the argument %VAL(PTR1) is equivalent to actually passing the data array whose address is stored in PTR1.

           CALL ADDIT (NELM, %VAL(PTR1), VALUE, STATUS)

An array of the correct size can then be declared in the subroutine ADDIT thus:

        SUBROUTINE ADDIT (NELM, A, VALUE, STATUS)
        INTEGER NELM
        REAL A(NELM)

ADDIT now operates directly on the array in memory just as if it had received it in the normal way.

In this example the array is mapped for ’UPDATE’ so when it is ‘unmapped’, the modified array is automatically written back into the NDF. (There is no explicit ‘unmap’ call in the example shown here, because the NDF_END will automatically annul the NDF1 identifier, and this unmaps any mapped arrays associated with that identifier.)

Several points should be noted:

(1)
In this example the program can modify the array as it was mapped for ’UPDATE’. An array mapped for ’READ’ can be read but not modified; mapping with ’WRITE’ access reserves an area in memory of appropriate size, but the array will be undefined until something is written to it.
(2)
The program appears to assume the data array is 1-dimensional. In fact it need not be so – a mapped array of any dimensions is just a number of values stored in successive memory locations which can be considered as a 1-d array. In the example shown there is no need to consider the actual dimensions of the input data array.

If you are not convinced you can compile and link the program and try it on the 1-d SPECTRUM.SDF and 2-d IMAGE.SDF. (All the necessary files can be copied from ADAM_EXAMPLES.) Doing a TRACE on these files before and after program execution should confirm that the addition has been carried out.

9There is nothing in the Fortran standard to enforce this passing by address, so the method is not guaranteed to work on any computer.

10This passing by value is used when interfacing Fortran with C routines; the latter might need to receive not the address of a variable, but its actual value. The %VAL extension is supported by compilers on both SUN and Convex machines.