Fortran77


Using the F77 Interface

To use the F77 interface you must link to ieeeio.a and the C++ libraries using
All machines except SGI Irix 6.4
f77 link_line_stuff -L$IEEE_DIRECTORY/lib -lieeeio -lC -lc
SGI machines running Irix 6.4 (eg. O2k and Octane)
f77 link_line_stuff -L$IEEE_DIRECTORY/lib -lieeeio -lC -lCsup -lc
The -lC and -lc links in the C and C++ libraries with the fortran libraries. Irix 6.4 also requires -lCsup for reasons I do not know.

If you also want HDF support you must also link with libhdfio.a and the usual complement of HDF libraries using -L$HDF_DIRECTORY/lib -L$IEEE_DIRECTORY/lib -lhdfio -lieeeio -lmfhdf -ldf -ljpeg -lz.


F77 Subroutines


Opening

Just like the familiar FILE* type for F77 stdio operations, all IEEEIO operations require a file handle. The type of this handle is INTEGER*8 (and 8-byte integer number). The ieee_open() and hdf_open() routines are used to create this handle and the io_close() subroutine can be used to close file handles of either type.

F77 Call Format
INTEGER*8 ieee_open(CHARACTER filename(*),CHARACTER accessmode(*))
INTEGER*8 ieee_openr(CHARACTER filename(*))
INTEGER*8 ieee_openw(CHARACTER filename(*))
INTEGER*8 ieee_opena(CHARACTER filename(*))

filename:
The name of the IEEEIO data file to open. The typical extension for these files is .raw.
accessmode
This is the type of access for the file. You can either pass an accesstype option to the function or use the appropriately named function for the type of access you desire.
After you open the file handle you can use the same set of subroutines for operations on the file regardless of whether the file is HDF or IEEEIO. The libraries manage all of this internally. So the open step is the only thing that is important to differentiating between your HDF and IEEEIO files. There are plans, for example, to have a SocketIO system that allows the data to be written out to a TCP socket instead of to a file for real-time simulation-visualization systems. There are also plans to use this interface to drive an existing Parallel IO system.


c The file handles are INTEGER*8's
    INTEGER*8 writer
    INTEGER*8 reader
c open a file for writing
    writer = ieee_openw('datafileout.raw')
c open an HDF file for writing
    writer = hdf_openw('datafileout.raw')
c open HDF file for reading  
    reader = hdf_openr('datafilein.raw')
c open an IEEEIO file for reading
    reader = ieee_openr('datafileout.raw')
      

You can test if the file was opened successfully using the io_isvalid() function.

    IF io_isvalid(infile).EQ.0 THEN
    	write(*) 'The file you specified does not exist or is not in a
readable format'
      

To close the file, you simply use io_close.
   INTEGER io_close(INTEGER*8 filehandle)
      

Writing

To write data you simply use the method write().

F77 Call Format
INTEGER io_write(INTEGER*8 filehandle,INTEGER numbertype,INTEGER rank,INTEGER dimensions(*),type data(*))

filehandle
An open filehandle for the datafile.
numbertype:
The type of the data being stored (datatype definition). It can be one of
rank
Number of dimensions of the dataset
dimensions:
An array of rank integers that give the dimensions of the dataset
data:
Your data array.
So to write a sample array of data.
	REAL*4 myarray(40,50,60) // our bogus data array
	INTEGER i,rank
	INTEGER dims(3)
	rank=3
	dims(1)=40
	dims(2)=50
	dims(3)=60
c this is because ieeeio assumes f77 order for data 
c and c/c++ use exactly the opposite ordering for data 
c in memory.
c   create a outfile
	INTEGER*8 writer,ieee_openw
	writer = ieee_openw('datafile.raw')
	DO i=0,ndatasets
		. . . . computation . . .
c 		write a dataset 
		CALL io_write(writer,FLOAT32,rank,dims,myarray)
		. . . . you can write as many datasets as you want
	ENDDO
c then close the file 
	CALL io_close(writer) 
      

Reading Data

Reading is a two step process. First you get information on the size and type of the data you intend to read. This allows you to allocate an array of the proper size and type for the reading. Then you actually read the data into a pre-allocated array. The methods for this are io_readInfo() and io_read().

F77 Call Format
INTEGER io_readInfo(INTEGER*8 filehandle,INTEGER numbertype,INTEGER rank,INTEGER dims(*),INTEGER maxdims)

filehandle
A filehandle open for reading or appending.
numbertype:
The type of the data being stored (datatype definition). It can be one of
rank
Number of dimensions of the dataset
dimensions:
An array of rank integers that give the dimensions of the dataset
maxdims:
The maximum size of the dimensions array you given it. This prevents array overruns if the dataset has more dimensions than you were anticipating. It can be any positive integer.
This retrieves information about the datatype, rank, and dimensions of the dataset to be retrieved. By default the maximum size of the dimensions array is 3, but you can set it to be larger.

F77 Call Format
INTEGER io_read(INTEGER*8 filehandle,sometype data(*))

This actually reads the dataset into the preallocated array data.

Another useful utility function is io_sizeof() which returns the number of bytes in a give IO datatype in manner analogous to the standard C sizeof() operator. This may not be of consequence for most f77 codes. So for instance, to read a simple dataset, you would do

	INTEGER rank
	INTEGER numbertype
	INTEGER dims(3)
c Note: The "PTR" is the architecture dependent definition of
c a dynamically-allocated memory handle. (my vary from system to
c system under F77.
	PTR data
	INTEGER*8 infile,ieee_openr
	infile = ieee_openr('dataset.raw')
	io_readInfo(infile,numbertype,rank,dims)
c OK, we are assuming a 3D IO::Float32 array, 
c but you can be more sophisticated... 
c Note: The "allocate" statement is a stand-in for
c your architecture dependent dynamic memory allocation function
c for your particular system.
        data = allocate(io_nbytes(numbertype,rank,dims))
c io_nbytes() is a convenience function.. you can just as easily use
c	data = allocate(dims(1) * dims(2) * dims(3) * io_sizeof(numbertype))
	CALL io_read(infile,data) // read in the data
      

Since multiple datasets can be stored in a file, you can retrieve them in the order they were written (there is a seek() function that allows random access as well). The method io_readinfo() implies reading the next dataset stored in the file. The method io_numdata() tells how many datasets are in a file. So typically if you want to read all datasets in a file in order, you would use code similar to;

	INTEGER*8 infile,ieee_openr
	infile = ieee_openr('dataset.raw')
	ndatasets = io_numdata(infile)
	DO i=1,ndatasets BEGIN
	  .....lots of code....
c increments to next dataset
	  CALL io_readinfo(infile,numbertype,rank,dims) 
	  .....more code....
        ENDDO
      

Random Access to Datasets (Seeking)

You can select specific datasets in a file using the seek() method.

F77 Call Format INTEGER io_seek(INTEGER*8 filehandle,INTEGER index)

filehandle:
Handle to a file open for reading.
index:
The index of the dataset you want to read from. This can be any number from 0 to (number_of_datasets - 1).

Writing Attributes

Attributes allow you to attach extra information to each dataset stored in the file. Each attribute has a name and an array of data (of any of the standard IO datatypes) stored with it. These attributes can be retrieved by name or by integer index in the order in which they were stored. A typical attribute would typically be parameters that describe the grid or the data like, 'origin' which would be the 3-vector of floats which locates of the origin of a grid in 3-space. The method used to write these attributes is io_writeAttribute().

F77 Call Format
INTEGER io_writeatt(INTEGER*8 filehandle,CHARACTER name(*),INTEGER numbertype,INTEGER length,(some type) data(*))

filehandle
An open filehandle for the datafile.
name:
Name of the attribute (like 'origin' or 'coordinates')
numbertype:
The type of the data array associated with the attribute (datatype definition)
length:
The number of elements in the data array.
data:
The attribute data.

So to write an attribute named origin along with a 3-vector float for the coordinates of the origin, you would use;

	REAL*4 origin(3)
	origin(1)=5.
	origin(2)=0.3
	origin(3)=0.5
c and assuming the file is already open for writing
c the following attribute will be attached to the last 
c written dataset. (you must have write data before adding attribs)
	io_writeatt(writer,'origin',FLOAT32,3,origin)
      

Reading Attributes

The attributes can be retrieved in the order they were written or they can be retrieved by their name. To retrieve the attributes in order, you would utilize the io_numatt() method to determine how many attributes are attached, io_iattinfo() to get the size and type of the attribute based on its index (use io_attinfo() to get size and type for the attribute based on its name), and io_readatt() to read the attribute data.

F77 Call Format
INTEGER io_numatt(INTEGER*8 filehandle)

filehandle
An open filehandle for the datafile.
returnvalue:
Number of attributes in the file

F77 Call Format
INTEGER io_iattinfo(INTEGER index,CHARACTER name(*),INTEGER numbertype,INTEGER length,INTEGER maxnamelength)

index:
The index of the attribute which can be 0 to (nattributes-1)
name:
A buffer in which the name of the attribute will be placed.
numbertype:
The type of the attribute data (datatype definition)
length:
The number of elements in the attribute data.
maxnamelength:
The maximum size of a name that can be stored in the name buffer. It can be any positive integer.

F77 Call Format
INTEGER io_readatt(INTEGER*8 filehandle,INTEGER index,(sometype) data(*))

filehandle
An open filehandle for the datafile.
index:
The index of the attribute data to read
data:
The array into which the attribute data is copied.
So for example, to read the attributes in order, you can use
    INTEGER io_numatt
    CHARACTER*128 name
    INTEGER length,datatype
    nattributes=io_numatt(infile)
	DO i=0,nattributes
	    ...
	    CALL io_iattinfo(infile,i,name,&datatype,&length)
	    ... allocate some data for storage or put in preallocated data
	    CALL io_readatt(infile,i,data)
	ENDDO
      

The attributes can also be retrieve by name. In fact, the is the most likely way you will use the attibutes interface. The io_attinfo() method is overloaded to allow retrieval by name as well. It returns the index of the attribute if one is found with a matching name: it returns -1 if one is not found.

F77 Call Format
INTEGER io_attinfo(CHARACTER name(*),INTEGER numbertype,INTEGER length)

filehandle
An open filehandle for the datafile.
returnvalue:
The index of the attribute if found or -1 if no attribute with matching name is found.
name:
The name of the attribute to find.
numbertype:
Returns the numbertype of the stored attribute data (datatype definition)
length:
The length of the stored attribute data.
So a typical use of this interface would be to find an attribute named 'origin' and retrieve its data if it exists.
	INTEGER io_attinfo
	index = io_attinfo(infile,'origin',datatype,length)
	IF (index.LE.0) THEN 
c the attribute exists
		CALL io_readatt(infile,index,data)
	ELSE
		write (*) 'The attribute origin could not be found'
	ENDIF
      

Writing Annotations

An annotation is a text string which can be used to describe a dataset. To write an annotation, you use the writeAnnotation() method.

F77 Call Format
INTEGER io_writenote(INTEGER*8 filehandle,CHARACTER annotationtext(*),INTEGER length)

filehandle
An open filehandle for the datafile.
annotationtext:
A character array of the annotation text
annotationlength
The length of the character array
The annotation will be attached to the last written dataset. You can store more than one annotation per dataset and the annotations can be of arbitrary length.


Reading Annotations

The annotations are stored in the order they are written. The method io_numnote() is used to find out how many attributes are attached to a dataset. The method io_readnoteinfo() is used to find the length of the annotation and io_readnote() reads the actual annotation text.

F77 Call Format
INTEGER io_numnote(INTEGER*8 filehandle)

filehandle
An open filehandle for the datafile.
returnvalue:
Number of annotations attached to current dataset.

F77 Call Format
io_readnoteinfo(INTEGER*8 filehandle,INTEGER index,INTEGER length)

filehandle
An open filehandle for the datafile.
index:
Index of the annotations which can be 0 to (nannotations-1)
length:
Length in characters of the annotation. This includes the null-terminating character.

Writing and Reading in Chunks

For distributed-memory programming paradigms like HPF, MPI, or PVM, it is often not unfeasible to write data to disk in a single operation. For this reason, a chunking interface is provided which allows you to write data in blocks to the disk.

To begin a chunk writing operation, you must first reserve a data chunk in the file. This is accomplished using io_reservck() F77 Call Format
INTEGER io_reservck(INTEGER*8 filehandle,INTEGER datatype,INTEGER rank,INTEGER dims(*))

Once space has been allocated in the datafile, you can write blocks of data specified by their dimensions and origin using io_writeck() F77 Call Format
INTEGER io_writeck(INTEGER*8 filehandle,INTEGER dims(*),INTEGER origin(*),(sometype) data(*...))

Likewise, it is possible to read chunks from the disk as well. No special procedure is required to select a record to read in chunks. Simply use io_readinfo() to get the dimensions and type of the dataset and then use io_readck() in place of io_read() in order to read-in the data. F77 Call Format
INTEGER io_readck(INTEGER*8 filehandle,INTEGER dims(*),INTEGER origin(*),(sometype) data(*...))


John Shalf
Last modified: Thu Feb 4 22:02:39 CST 1999