FXDR

David W. Pierce
Scripps Instititution of Oceanography

What is it?

FXDR is a library that allows you to make calls to the XDR (eXternal Data Representation) routines from Fortran. You almost certainly already have the XDR routines on your workstation--with FXDR you can easily call them from Fortran. In practical terms, this means that you can read and write unformatted binary files in your Fortran code that are portable between different platforms. Write your binary on a Cray, read it on a DEC, write it on an SGI, read it on a HP--it doesn't matter.

  • Download the source for FXDR version 2.1b. I've tested it on DEC Alphas and PMAX, Crays, SGIs, and HPs. Version 2.1c released 25 Jan, 2002.
  • SPECIAL NOTE FOR VERSION 1.x USERS

    (change log).

    click here to read Belorussian translation (provided by Fatcow)

    More detailed information on FXDR

    Table of Contents

    1. What is FXDR?
    2. Why would I want to use the XDR routines via the FXDR library?
    3. How do I use FXDR?
    4. More detail on using the FXDR library
    5. An important note on precision
    6. Yet another note on precision
    7. Interpreting the results of the test program
    8. More complete information
    9. Copyright

    What is FXDR?

    The FXDR library is a set of Fortran routines that provide an interface to the XDR routines that already exist (almost certainly) on your workstation or mainframe. I.e., it provides an easy and convenient way to use the XDR ability of your computer from Fortran.

    Back to Table of Contents

    Why would I want to use the XDR routines via the FXDR library?

    In a single sentence: if you use the FXDR routines in your Fortran codes to write out your binary data files, then you can move those data files around to any of the different machines you use, and the data will be read in correctly! No more translating between different platforms! No more huge ASCII files to deal with! OK, that was more than a single sentence, but I got enthusiastic.

    The XDR library is what computers use to talk to each other for RPC (Remote ProCedure) calls over the network. When RPC was first designed, the makers realized that different vendor's computers couldn't easily talk to each other over a network because of byte ordering and precision differences. For example, binary data written on a Sun can't be directly read on a DEC because the byte ordering is opposite on the two; and Cray floating point binary data can't easily be read on anything else because Cray uses their own format, not IEEE (as most everyone else uses). So, the RPC designers standardized a particular way of representing data (the eXternal Data Representation, or XDR).

    If you write out data using the XDR libraries, then read it in using the XDR libraries, then it doesn't matter what platform you either wrote it on or read it on. You can write it on the Sun, and read it on the DEC, or write it on a Cray and read it on an SGI. As long as you go through the XDR library, the low level format of the data will be understood correctly. (You still have to know the high level format of the data. Which is to say, if you wrote a single precision floating point number, you must instruct the routines to read in a single precision floating point number and not something different such as a integer or character string.)

    Back to Table of Contents

    How do I use FXDR?

    You just replace your Fortran unformatted binary write statements with calls to the appropriate FXDR library routine.

    Example:

    Replace

            parameter (nx=80,ny=100)
            real    coeffs(nx,ny)
            open(ilun, file='testdata', form='unformatted')
            write(ilun) coeffs
    

    With

            parameter (nx=80,ny=100)
            real    coeffs(nx,ny)
            ixdrs = initxdr('testdata.xdr', 'w', .FALSE.)
            ierr  = ixdrrmat( ixdrs, nx*ny, coeffs )
    

    Pretty easy, eh? To read in that same data:

    Replace

            open(ilun, file='testdata', form='unformatted')
            read(ilun) coeffs
    

    With

            ixdrs = initxdr('testdata.xdr', 'r', .FALSE.)
            ierr = ixdrrmat( ixdrs, nx*ny, coeffs )
    

    Note that the call to the FXDR routine is exactly the same whether you are reading or writing; whether you actually read, write, or append is determined by whether you called the 'initxdr' routine with a 'r', 'w', or 'a' as the second argument. This is an improvement over Fortran, where the call to open the file is exactly the same whether you are reading or writing, but you call different routines ('read' or 'write') to accomplish the one you want. The third argument to initxdr controls error handling; for the most Fortran-like behavior, set it to .FALSE., in which case the XDR routines will print out an error message and stop if they encounter an error (this is similar to what Fortran does). If the third argument is set to .TRUE., then the XDR routines will return a value less than zero if they encounter an error, allowing your calling code to specifically handle this case.

    Back to Table of Contents

    More detail on using the FXDR library

    To use the FXDR library you first open the XDR-format file using the 'initxdr' routine. This routine returns an integer that is the ID number all the XDR routines will use to indicate which XDR file you want to work with. The first argument to 'initxdr' is the name of the file; the second argument is 'r', 'w', or 'a' for reading, writing, or appending; the third argument is .FALSE. if you want the routines to print an error message and halt if they encounter an error (like Fortran does), and .TRUE. if you want the routines to return an error code that is less than zero if they encounter an error:

            character*(*) filename
            character*1   mode
            integer       id
            logical       return_on_error
            id = initxdr( filename, mode, return_on_error )
    

    Then you replace both your 'read' and 'write' calls with a call to one of the following integer functions:

    Name      Bits  Fortran type of argument
    --------- ----  --------------------------------------------------------
    ixdrdouble 64    double precision floating point number
    ixdrdmat   64    array of double precision floating point numbers
    ixdrint    32    integer
    ixdrimat   32    array of integers
    ixdrreal   32    single precision floating point number
    ixdrrmat   32    array of single precision floating point numbers
    ixdrreal64 64    single precision floating point number
    ixdrrmat64 64    array of single precision floating point numbers
    ixdrstring n/a   character*(*)
    

    The 'Bits' column shows how many significant bits are saved in the XDR file.

    Return values for all routines: if initxdr was originally called for this file with the third argument .FALSE., then an error causes the routine to print an error message and stop. If initxdr was called with the third argument .TRUE., then these routines return no matter what, and the value returned is less than 0 if an error was encountered.

    The scalar (i.e., single value, not array) routines all take exactly two parameters:

  • the xdr file ID, which was the integer returned by the 'initxdr' call.
  • the variable to either read or write.
  • The array routines all take exactly three parameters:

  • the xdr file ID, which was the integer returned by the 'initxdr' call.
  • the TOTAL number of elements in the array.
  • the array to either read or write.
  • Back to Table of Contents

    An important note about precision

    It is IMPORTANT to understand that the precision referred to above (for example, the 'single' precision of routine 'ixdrreal' or the 'double' precision of routine 'ixdrdouble') is SET BY YOUR COMPILER, not by the FXDR library. This matters, because different compilers have different default precisions (as in, number of bits) associated with the names 'single', 'double', etc. At the time of writing this, this makes the biggest difference when moving XDR files between Crays and workstations such as Suns, HPs, or DECs, because by defalt the Cray Fortran compiler uses a 'single' precision of 64 bits and a 'double' precision of 128 bits, while the Sun, HP, and DEC use a 'single' precision of 32 bits and a 'double' precision of 64 bits. (You might get a similar effect on a workstation if you compile with a flag such as '-r8', which instructs the compiler to make all 'single precision' variables default to 8 bytes [64 bits] of precision).

    The upshot is that if you use a Cray to write a Fortran single precision floating point number into an XDR file using routine 'ixdrreal', only 32 bits of the original 64 bits will be written. But, you can't use routine 'ixdrdouble' to write all 64 bits correctly, because that routine expects to be passed a Fortran double precision floating point variable, not a single precision variable.

    To get around this, there is a set of special routines that take 'single precision' variables as defined by the Fortran compiler and write them out with 64 bits (these are the 'ixdrreal64' and 'ixdrrmat64' routines). To read these in on a workstation where a real is only 32 bits, you should use the 'ixdrdouble' or 'ixdrdmat' routines and supply a *double* precision floating point variable to receive the 64 bits of valid data. (To read these in on a Cray, you just use the same routine that wrote the data out in the first place--'ixdrreal64' or 'ixdrrmat64').

    To sum up for 64-bit precision:

    Write single precision values on a Cray (or workstation with 64 bit Fortran reals) with 'ixdrreal64' or 'ixdrrmat64', then read them into double precision values on a regular workstation with a call to 'ixdrdouble' or 'ixdrdmat'.

    Write double precision values on a workstation with 'ixdrdouble' or 'ixdrdmat', then read them into single precision values on a Cray (or workstation with 64 bit Fortran reals) with a call to 'ixdrreal64' or 'ixdrrmat64'.

    Note that you don't have to use 64-bit precision; you can use 32-bit precision if you want (I often do, the files are half as big). This is easy to do; just write out real*4 type numbers on both the Cray and the workstation, and use the 32-bit calls such as ixdrrmat and ixdrreal.

    Back to Table of Contents

    Yet another important note about precision

    Unfortunately, that's not all you should know about precision. There is also the problem that Crays (at the moment--new Crays might change this) use a completely different scheme for representing numbers internally than do workstations. Crays use "Cray floating point format" (duh) while workstations use "IEEE floating point format". Well, the underlying XDR libraries that FXDR calls always use IEEE floating point format, so if you always work on a workstation then everything is fine and please ignore this paragraph despite the fact that you've already read a lot of it. If you work on Crays, though, you need to be aware that putting data into an XDR file and then immediately reading it back in WILL NOT leave you with exactly the same floating point number that you started with. It will likely be different in the last bit position or so. This is a tiny difference, and to put it into perspective, the Cray compiler will itself do things to your code (and warn you that it is doing them) that can change your results this much, for the sake of making the code run faster. However if you just can't live with losing a bit of precision in your numbers then please erase your copy of this library and for God's sake start drinking decaffinated coffee!

    Seriously, though, the kind of place where this can make a difference is if you have a long numerical simulation run and are writing a restart file partway through, but still intend to run on only one platform. In such a case, you'd likely be better off NOT using FXDR to write the restart file, as you won't be able to get bit reproducibility between a run in which the model was restarted partway through, and a run in which the model was never restarted. But this is a pathological case in a way; if you are using FXDR, it's because you want to move results between platforms. If you move your model run between platforms, you will NOT get bit reproducibility no matter what, so using FXDR doesn't cost you anything anyway. In my opinion, the best use for FXDR is for model initialization and output files, not restart files; having your initialization files in FXDR is very convenient, because you can move them transparently to every platform you want to run on; and having the output in FXDR is great, because you can analyze the results on whatever machine is most convenient. If you are already moving restart files between platforms and continuing runs on different machines, then you have already lost bit reproducibility anyway, so you might as well use FXDR for the restart files, but in general I would advise against taking this approach for restart files.

    Back to Table of Contents

    Interpreting the results of the test program

    There is a test program included with the distribution. When you "make test" then the program (called--if you can possibly believe it--"test.F") is compiled and runs, testing the FXDR library. It:

  • Writes out a new .xdr file, called "test.xdr"
  • Reads in that file that it just created ("test.xdr") and checks to see if it has the expected values.
  • Reads in a .xdr file that I generated on a DEC alpha ("test.orig.xdr") to see if it has the expected values.
  • Now what often happens is that there is some precision loss somewhere along the way, usually in the last bit. The first part of the test, which creates a .xdr file and then reads it back in, rarely shows any loss of precision on a machine that itself uses IEEE floating point format (most workstations including DEC, Sun, HP, and SGI). So frequently that part will pass with no warnings of precision loss. On the other hand, if you run it on a Cray (which does NOT use IEEE floating point format) then you will see some loss of precision at this point. For single precision floating point values, 1 part in 10**7 is about the last bit; for double precision floating point values, 1 part in 10**14 is about the last bit. So if you see losses of precision of this amount or smaller (and you almost certainly WILL) then it just means that the last bit is getting munged. You have to decide for yourself whether this is a problem or if you can live with it. Remember, the settings of compiler flags affecting the optimization level frequently alter your results to this level of precision. As noted above, I see this mostly as an issue of restart files versus initialization or output files.

    The second part of the test reads in a .xdr file that I generated on a DEC alpha and checks it for the proper values. Unless you happen to be running on a DEC alpha yourself, then you will almost certainly get some warnings of loss of precision at this point.

    Back to Table of Contents

    Complete information

    There is more complete information on how to build, install, and configure the FXDR library in the README file, so be sure to look through that after downloading the source. Also, don't forget to look at the man pages (man fxdr).

    Back to Table of Contents

    Version Information

    2.1c: Jan 25, 2002. Added AIX to the platforms supported by 'configure'.

    2.1b: Sep 27, 1999. Minor fixes for returned error values. Added Linux to the platforms supported by 'configure'.

    2.1a: Sep 2, 1999. Fix for misnamed variable when compiling on an HP. Thanks to Aldo Bonfiglioli for this.

    2.1: Aug 31, 1999. Added support for newer Crays that use two-word Fortran character string pointers.

    2.0: Aug 10, 1999. Totally changed the interface to support error handling. Routines are no longer backwards compatable with version 1.x code.

    1.4 to 1.4a: Nov 13, 1998. Minor modifications to 'unicos' system defines for newer Cray compilers.

    1.3 to 1.4: Nov 14, 1997. Added support for 'short' type. Added more information for users of HP, Linux, and sun systems.

    1.2 to 1.3: July 213, 1996. Added check to 'mode' passed to routine 'initxdr'.

    1.1 to 1.2: March 18, 1996. Fixed bug in makefile. Added "local_filename" to routine "cxdrinit.c" to make it work under UNICOS 9.0.1 on the C90.

    1.0 to 1.1: November 28, 1995. Added routine 'xdrrewind'.

    Back to Table of Contents

    Copyright

    Copyright (c) 1995 The Regents of the University of California. All rights reserved.

    Redistribution and use in source and binary forms are permitted provided that (1) source distrubutions retain this entire copyright notice and comment, and (2) distributions including binaries display the following acknowledgement: ``This product includes software developed by the University of California, San Diego and its contributors'' in the documentation or other materials provided with the distribution and in all advertising materials mentioning features or use of this software. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

    Back to Table of Contents

    Back to David Pierce's home page.

    Last modified Sep 2, 1999