Loading...
Searching...
No Matches
WCSLIB Fortran wrappers

The Fortran subdirectory contains wrappers, written in C, that allow Fortran programs to use WCSLIB. The wrappers have no associated C header files, nor C function prototypes, as they are only meant to be called by Fortran code. Hence the C code must be consulted directly to determine the argument lists. This resides in files with names of the form `*_f`.c. However, there are associated Fortran `INCLUDE` files that declare function return types and various parameter definitions. There are also `BLOCK` `DATA` modules, in files with names of the form `*_data`.f, used solely to initialise error message strings.

A prerequisite for using the wrappers is an understanding of the usage of the associated C routines, in particular the data structures they are based on. The principle difficulty in creating the wrappers was the need to manage these C structs from within Fortran, particularly as they contain pointers to allocated memory, pointers to C functions, and other structs that themselves contain similar entities.

To this end, routines have been provided to set and retrieve values of the various structs, for example `WCSPUT` and `WCSGET` for the wcsprm struct, and `CELPUT` and `CELGET` for the celprm struct. These must be used in conjunction with wrappers on the routines provided to manage the structs in C, for example `WCSINIT`, `WCSSUB`, `WCSCOPY`, `WCSFREE`, and `WCSPRT` which wrap wcsinit(), wcssub(), wcscopy(), wcsfree(), and wcsprt().

Compilers (e.g. gfortran) may warn of inconsistent usage of the third argument in the various `*PUT` and `*GET` routines, and as of gfortran 10, these warnings have been promoted to errors. Thus, type-specific variants are provided for each of the `*PUT` routines, `*PTI`, `*PTD`, and `*PTC` for int, double, or char[], and likewise `*GTI`, `*GTD`, and `*GTC` for the `*GET` routines. While, for brevity, we will here continue to refer to the `*PUT` and `*GET` routines, as compilers are generally becoming stricter, use of the type-specific variants is recommended.

The various `*PUT` and `*GET` routines are based on codes defined in Fortran include files (*.inc). If your Fortran compiler does not support the `INCLUDE` statement then you will need to include these manually in your code as necessary. Codes are defined as parameters with names like `WCS_CRPIX` which refers to wcsprm::crpix (if your Fortran compiler does not support long symbolic names then you will need to rename these).

The include files also contain parameters, such as `WCSLEN`, that define the length of an `INTEGER` array that must be declared to hold the struct. This length may differ for different platforms depending on how the C compiler aligns data within the structs. A test program for the C library, twcs, prints the size of the struct in sizeof(int) units and the values in the Fortran include files must equal or exceed these. On some platforms, such as Suns, it is important that the start of the `INTEGER` array be aligned on a `DOUBLE` `PRECISION` boundary, otherwise a mysterious `BUS` error may result. This may be achieved via an `EQUIVALENCE` with a `DOUBLE` `PRECISION` variable, or by sequencing variables in a `COMMON` block so that the `INTEGER` array follows immediately after a `DOUBLE` `PRECISION` variable.

The `*PUT` routines set only one element of an array at a time; the final one or two integer arguments of these routines specify 1-relative array indices (N.B. not 0-relative as in C). The one exception is the prjprm::pv array.

The `*PUT` routines also reset the flag element to signal that the struct needs to be reinitialized. Therefore, if you wanted to set wcsprm::flag itself to -1 prior to the first call to `WCSINIT`, for example, then that `WCSPUT` must be the last one before the call.

The `*GET` routines retrieve whole arrays at a time and expect array arguments of the appropriate length where necessary. Note that they do not initialize the structs, i.e. via wcsset(), prjset(), or whatever.

A basic coding fragment is

          INTEGER   LNGIDX, STATUS
          CHARACTER CTYPE1*72

          INCLUDE 'wcs.inc'

    *     WCSLEN is defined as a parameter in wcs.inc.
          INTEGER   WCS(WCSLEN)
          DOUBLE PRECISION DUMMY
          EQUIVALENCE (WCS, DUMMY)

    *     Allocate memory and set default values for 2 axes.
          STATUS = WCSPTI (WCS, WCS_FLAG, -1, 0, 0)
          STATUS = WCSINI (2, WCS)

    *     Set CRPIX1, and CRPIX2; WCS_CRPIX is defined in wcs.inc.
          STATUS = WCSPTD (WCS, WCS_CRPIX, 512D0, 1, 0)
          STATUS = WCSPTD (WCS, WCS_CRPIX, 512D0, 2, 0)

    *     Set PC1_2 to 5.0 (I = 1, J = 2).
          STATUS = WCSPTD (WCS, WCS_PC, 5D0, 1, 2)

    *     Set CTYPE1 to 'RA---SIN'; N.B. must be given as CHARACTER*72.
          CTYPE1 = 'RA---SIN'
          STATUS = WCSPTC (WCS, WCS_CTYPE, CTYPE1, 1, 0)

    *     Use an alternate method to set CTYPE2.
          STATUS = WCSPTC (WCS, WCS_CTYPE, 'DEC--SIN'//CHAR(0), 2, 0)

    *     Set PV1_3 to -1.0 (I = 1, M = 3).
          STATUS = WCSPTD (WCS, WCS_PV, -1D0, 1, 3)

          etc.

    *     Initialize.
          STATUS = WCSSET (WCS)
          IF (STATUS.NE.0) THEN
            CALL FLUSH (6)
            STATUS = WCSPERR (WCS, 'EXAMPLE: '//CHAR(0))
          ENDIF

    *     Find the "longitude" axis.
          STATUS = WCSGTI (WCS, WCS_LNG, LNGIDX)

    *     Free memory.
          STATUS = WCSFREE (WCS)

Refer to the various Fortran test programs for further programming examples. In particular, twcs and twcsmix show how to retrieve elements of the celprm and prjprm structs contained within the wcsprm struct.

Treatment of `CHARACTER` arguments in wrappers such as `SPCTYPE`, `SPECX`, and `WCSSPTR`, depends on whether they are given or returned. Where a `CHARACTER` variable is returned, its length must match the declared length in the definition of the C wrapper. The terminating null character in the C string, and all following it up to the declared length, are replaced with blanks. If the Fortran `CHARACTER` variable were shorter than the declared length, an out-of-bounds memory access error would result. If longer, the excess, uninitialized, characters could contain garbage.

If the `CHARACTER` argument is given, a null-terminated `CHARACTER` variable may be provided as input, e.g. constructed using the Fortran `CHAR(0)` intrinsic as in the example code above. The wrapper makes a character-by-character copy, searching for a NULL character in the process. If it finds one, the copy terminates early, resulting in a valid C string. In this case any trailing blanks before the `NULL` character are preserved if it makes sense to do so, such as in setting a prefix for use by the `*PERR` wrappers, such as `WCSPERR` in the example above. If a NULL is not found, then the `CHARACTER` argument must be at least as long as the declared length, and any trailing blanks are stripped off. Should a `CHARACTER` argument exceed the declared length, the excess characters are ignored.

There is one exception to the above caution regarding `CHARACTER` arguments. The `WCSLIB_VERSION` wrapper is unusual in that it provides for the length of its `CHARACTER` argument to be specified, and only so many characters as fit within that length are returned.

Note that the data type of the third argument to the `*PUT` (or `*PTI`, `*PTD`, or `*PTC`) and `*GET` (or `*GTI`, `*GTD`, or `*GTC`) routines differs depending on the data type of the corresponding C struct member, be it int, double, or char[]. It is essential that the Fortran data type match that of the C struct for int and double types, and be a `CHARACTER` variable of the correct length for char[] types, or else be null-terminated, as in the coding example above. As a further example, in the two equivalent calls

          STATUS = PRJGET (PRJ, PRJ_NAME, NAME)
          STATUS = PRJGTC (PRJ, PRJ_NAME, NAME)

which return a character string, `NAME` must be a `CHARACTER` variable of length 40, as declared in the prjprm struct, no less and no more, the comments above pertaining to wrappers that contain `CHARACTER` arguments also applying here. However, a few exceptions have been made to simplify coding. The relevant `*PUT` (or `*PTC`) wrappers allow unterminated `CHARACTER` variables of less than the declared length for the following: `prjprm::code` (3 characters), `spcprm::type` (4 characters), `spcprm::code` (3 characters), and `fitskeyid::name` (8 characters). It doesn't hurt to specify longer `CHARACTER` variables, but the trailing characters will be ignored. Notwithstanding this simplification, the length of the corresponding variables in the `*GET` (or `*GTC`) wrappers must match the length declared in the struct.

When calling wrappers for C functions that print to stdout, such as `WCSPRT`, and `WCSPERR`, or that may print to stderr, such as `WCSPIH`, `WCSBTH`, `WCSULEXE`, or `WCSUTRNE`, it may be necessary to flush the Fortran I/O buffers beforehand so that the output appears in the correct order. The wrappers for these functions do call `fflush(NULL)`, but depending on the particular system, this may not succeed in flushing the Fortran I/O buffers. Most Fortran compilers provide the non-standard intrinsic `FLUSH()`, which is called with unit number 6 to flush stdout (as in the example above), and unit 0 for stderr.

A basic assumption made by the wrappers is that an `INTEGER` variable is no less than half the size of a `DOUBLE` `PRECISION`.