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. ============================================================================= This is the FXDR library, version 1.0 November 5, 1995 David Pierce Climate Research Division Scripps Institution of Oceanography dpierce@ucsd.edu To make the FXDR library: ------------------------- 1. type "configure". If this fails, then see the section below titled "Configuring for unsupported systems". If you have already played around with the library some, please "make clean" BEFORE "configure"! 2. type "make" 3. If that worked, type "make test" to test the library. See also the section "Interpreting the results of make test", below. Basically, if you get the message "All tests passed successfully!", then the test passed even if (as usually happens) you have cases where you've lost one bit of precision. 4. If that worked, you can type "make install" to install the library and man page in the default locations (/usr/local/lib and /usr/man/manl). 5. Look at program "test.F" to see how to use the subroutines, or consult the man pages ("man fxdr"). 6. Link your own programs with '-lfxdr' at compile time to make everyone (or at least your compiler) happy. What is FXDR? ------------- The FXDR library is a set of FORTRAN routines which provide an interface to the XDR routines which 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. 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.) 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='test_data', form='unformatted') write(ilun) coeffs With parameter (nx=80,ny=100) real coeffs(nx,ny) ixdrs = initxdr('test_data.xdr', 'w') call xdrrmat( ixdrs, nx*ny, coeffs ) Pretty easy, eh? To read in that same data: Replace open(ilun, file='test_data', form='unformatted') read(ilun) coeffs With ixdrs = initxdr('test_data.xdr', 'r') call xdrrmat( 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 or write is determined by whether you called the 'initxdr' routine with a 'r' or 'w' as the second argument. This is opposite 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. 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 which is the ID number which 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, and the second argument is either 'r' or 'w' for reading or writing: character*(*) filename character*1 mode integer id id = initxdr( filename, mode ) Then you replace both your 'read' and 'write' calls with a call to one of the following routines: Name Bits FORTRAN type of argument --------- ---- -------------------------------------------------------- xdrdouble 64 double precision floating point number xdrdmat 64 array of double precision floating point numbers xdrint 32 integer xdrimat 32 array of integers xdrreal 32 single precision floating point number xdrrmat 32 array of single precision floating point numbers xdrreal64 64 single precision floating point number xdrrmat64 64 array of single precision floating point numbers xdrstring n/a character*(*) The 'Bits' column shows how many significant bits are saved in the XDR file. The scaler (i.e., single value, not array) routines all take exactly two parameters: 1) the xdr file ID, which was the integer returned by the 'initxdr' call. 2) the variable to either read or write. The array routines all take exactly three parameters: 1) the xdr file ID, which was the integer returned by the 'initxdr' call. 2) The TOTAL number of elements in the array. 3) the array to either read or write. An important note about precision --------------------------------- It is IMPORTANT to understand that the precision referred to above (for example, the 'single' precision of routine 'xdrreal' or the 'double' precision of routine 'xdrdouble') 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 'xdrreal', only 32 bits of the original 64 bits will be written. But, you can't use routine 'xdrdouble' 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 which take 'single precision' variables as defined by the FORTRAN compiler and write them out with 64 bits (these are the 'xdrreal64' and 'xdrrmat64' routines). To read these in on a workstation where a real is only 32 bits, you should use the 'xdrdouble' or 'xdrdmat' 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 which wrote the data out in the first place--'xdrreal64' or 'xdrrmat64'). To sum up: Write SINGLE PRECISION values on a Cray (or workstation with 64 bit FORTRAN reals) with 'xdrreal64' or 'xdrrmat64', then read them into DOUBLE PRECISION values on a regular workstation with a call to 'xdrdouble' or 'xdrdmat'. Write DOUBLE PRECISION values on a workstation with 'xdrdouble' or 'xdrdmat', then read them into SINGLE PRECISION values on a Cray (or workstation with 64 bit FORTRAN reals) with a call to 'xdrreal64' or 'xdrrmat64'. 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 which 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 which 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) which 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 this library and for God's sake start drinking decaffinated coffee. Interpreting the results of "make test" --------------------------------------- When you "make test" then a test program (called--if you can possibly believe it--"test.F") is compiled and runs, testing the FXDR library. It: 1) Writes out a new .xdr file, called "test.xdr" 2) Reads in that file which it just created ("test.xdr") and checks to see if it has the expected values. 3) Reads in a .xdr file which 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 which 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. The second part of the test reads in a .xdr file which 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. Linking your programs with the FXDR library ------------------------------------------- OK, you've written your FORTRAN program with calls to the FXDR library--now how do you get it to compile correctly? Well, here would be a typical command line: f77 niftyprogram.F -L/usr/local/lib -lfxdr the "-lfxdr" tells the compiler to link with the library named "libfxdr.a"; the "-L/usr/local/lib" tells the compiler to look for "libfxdr.a" in directory "/usr/local/lib". This is the default place to install the library. If you put the library somewhere else instead, then you will have to substitute the place where you put "libfxdr.a" in for "/usr/local/lib" in the typical command line given above. Configuring for unsupported systems ----------------------------------- To configure this package for an unsupported system, you have to do two things: 1) Create a file "Defines.unsupported" which contains lines like the following: F77CMD = f77 CCCMD = cc F77OPTS= CCOPTS = RANLIB = ranlib AR = ar CPP = /lib/cpp Basically, this tells the Makefile what to use for your FORTRAN compiler, your C compiler, what options to give those compilers, what to use for the 'ranlib' command (which convertes archives to random libraries; if your system doesn't have a ranlib command, try using "RANLIB = echo"), what to use for the archive builder, and what to use for the C preprocessor. You can look in the "Defines.xxx" files for the other systems to see some typical values. 2) Alter all the .c files to match the FORTRAN/C calling convention which your compiler uses. I imagine that most other compilers will alredy use a style which is already in the .c files, so you just have to add an "or" clause to the preprocessor check. For example, here is the beginning of file cxdrinit.c: ---------------------------------------------------------------------- #ifdef cray CXDRINIT( int *fname_len, char *filename, int *mode ) #elif defined(hpux) cxdrinit( fname_len, filename, mode ) int *fname_len; char *filename; int *mode; #else cxdrinit_( int *fname_len, char *filename, int *mode ) #endif ---------------------------------------------------------------------- If your Fortran compiler generates calls to "cxdrinit_" from source lines which look like "call cxdrinit", then you are all set; you don't have to do anything else. If your Fortran compiler generates calls in one of the other two styles, then you just replace (for example) the line: #elif defined(hpux) with: #elif (defined(hpux) || defined(my_systems_defined_symbol)) where "my_systems_defined_symbol" is replaced by some symbol which your C compiler defines for you. Check your C compiler's docs for a list of such defined symbols, although you aren't likely to find such a list because computer manufacturers seem to have a deathly fear of clear documenation. If this is the case you might try some "obvious" symbols based on the name of your system, such as "quagmire" if you are running on a Quagmire Coroporation ZXi Platinum Gold SE. If your Fortran compiler generates calls in some other style, you will have to add code to handle it to all the .c files. After you create the Defines.unsupported file, and make sure that your C code is properly called from the Fortran code, then type "configure unsupported" and then type "make" and keep your fingers crossed. Thank goodness this is the last topic ------------------------------------- Don't forget to look at the man pages (man fxdr). --Dave