wrapfort(n) 0.1 "Ftcl Library"

NAME

wrapfort - Quick wrapper for Fortran code

TABLE OF CONTENTS

    TABLE OF CONTENTS
    SYNOPSIS
    DESCRIPTION
    EXAMPLES
    COMMANDS
    FORTRAN 95 ASPECTS
    KEYWORDS

SYNOPSIS

package require Tcl 8.4
package require wrapfort 0.1

::Wrapfort::fsource pkgname filename
::Wrapfort::fproc tclname fortname description
::Wrapfort::fexternal interface description

DESCRIPTION

Wrapfort has a simple goal: make it easy to wrap the wealth of (numerical) software written in Fortran for use within Tcl programs. It is in a way akin to Critcl which does similar things for C, but Wrapfort does not necessarily involve on-the-fly compilation (see the note below).

The goal of Wrapfort is achieved in the following way:

This document describes the input to Wrapfort in detail, but to understand how to use it, you must have some idea of how it works and what ideas lie behind the generated code. Therefore we start with a few examples.

Note: Wrapfort does not differ that much in its approach to Critcl. In a next version it will be integrated with Critcl. The difference right now is that Wrapfort prepares all the files for building a shared library or DLL that can be loaded into Tcl as any loadable extension, but it does not actually start the build process. That way, you can select your own compiler and linker for instance.

EXAMPLES

The first example is very simple:

We can easily do this with Wrapfort: We use the fproc command to generate a C function that can be used as a Tcl command. This C function calls the Fortran routine to the actual work:

 
Wrapfort::fproc simple simple {
    integer x input
    integer y result
    code {Actually call the routine in this way} {
        simple( &x, &y );
    }
}

The first argument is the name of the corresponding Tcl command, the second argument is the name of the Fortran routine (they do not need to be the same). The third argument describes how the Tcl command must be generated: What happens when you run this is that fproc generates C term to store the contents of the first (and only) argument to the Tcl command in the local variable x. Then the Fortran routine simple is called, thus setting the local variable y to a value twice that of x.

As y is associated with the result of the Tcl command, the last part of the C code sets the result field in the Tcl interpreter to just that value.

Here is the result:

 
/* Wrapper for simple - Tcl-command Simple */
#ifdef FTN_UNDERSCORE
#   define simple simple_
#endif
#ifdef FTN_ALL_CAPS
#   define simple SIMPLE
#endif
void __stdcall simple();
static int c__simple( ClientData client_data, Tcl_Interp *interp,
                       int objc, struct Tcl_Obj * CONST objv[] ) {
    long x;
    long y;

    if ( objc != 2 ) {
        Tcl_SetResult( interp, "Wrong number of arguments", NULL );
        return TCL_ERROR;
    }

    if ( Tcl_GetLongFromObj( interp, objv[1], &x ) != TCL_OK ) {
        Tcl_SetResult( interp, "Argument 1 must be integer", NULL );
        return TCL_ERROR;
    }

        simple( &x, &y );


    Tcl_SetObjResult( interp, Tcl_NewLongObj(y) ) ;
/* Nothing to be done for: x */
/* Nothing to be done for: y */
    return TCL_OK;
}

The fproc command not only takes care of extracting the values from the Tcl_Objs that are passed and putting the result back, but also of checking the number of arguments, types of the arguments and cleaning up afterwards.

It also takes care of most platform-dependent aspects of interfacing C and Fortran.

The final result is a set of source files and a makefile that may need some tuning (select the right C and Fortran compilers) and then all is ready to build the DLL or shared library.

This was a particularly simple example. Let us look at something more complicated and practical: the MINPACK library (see: http://www.netlib.org).

The MINPACK library is a typical example of a set of numerical routines that can be very useful for a Tcl program too. It is also typical in the way the interfaces for such libraries have been designed. MINPACK is written in FORTRAN 77 and therefore you need to pass it several work arrays.

One routine in this library is HYBRD1. It attempts to find a root of a system of N (non-)linear equations in N variables. This routine has the following signature:

 
      SUBROUTINE HYBRD1(FCN,N,X,FVEC,TOL,INFO,WA,LWA)

where: Before we go on and describe the subroutine FCN, let us consider what a Tcl command wrapping this routine would have to look like. The subroutine FCN would become a Tcl procedure, the initial vector X would be a list of numbers, TOL could be an optional argument, defaulting to, say, 1.0e-6. We do not want to make the work arrays WA and FVEC visible, so they will be handled internally (as will the parameters N and LWA).

Rather than override the vector X with the final solution, we will return that solution as a list.

This means that the procedure, let us call it "findRoot", looks like:

 
    proc findRoot {fcn xinit {tol 1.0e-6}} {
        ...
        return $xfinal
    }

One issue remains: INFO. We have to decide how to provide the information stored in this parameter. Let us take a simple approach: The values 1 (found a solution) and 3 (tolerance too small) are regarded as good results. Anything else simply throws an error.

Here is a call to fproc to generate the wrapper for this routine:

 
Wrapfort::fproc findRoot hybrd1 {
    external fcn {}
    double-array x      input
    double       tol    {optional-input 1.0e-6}

    double-array fvec   {allocate  size(x)}
    integer      n      {assign    size(x)}
    double-array wa     {allocate  (n*(3*n+13))/2}
    integer      info   local
    integer      lwa    {assign    size(wa)}
    double-array xfinal {result    size(x)}
    integer      i      local

    code {Actually call the routine in this way} {
        hybrd1( fcn, &n, x, fvec, &tol, &info, wa, &lwa );
        for (i = 0; i < n; i ++) {
            xfinal[i] = x[i];
        }
        /* Check the result - set a message if there was an error */
        if ( info != 1 && info != 3 ) {
            WrapErrorMessage( "No satisfactory result achieved" );
        }
    }
}

Most of this should be clear from the description of the routine given above. We could have used the keyword input-output for the variable x, but by introducing the new array xfinal instead, we demonstrate that you can do more in the code section than just call the routine - and how to use local variables.

On return from the routine hybrd1 we check the variable info and set an error message via the macro WrapErrorMessage. This uses the predefined variable _rc_ to record the error. The wrapper routine continues its work, frees any allocated memory and then returns the right return code.

The second part of generating a wrapper for HYBRD1 is a bit more involved: the external routine FCN. It has a straightforward signature:

 
     SUBROUTINE FCN(N,X,FVEC,IFLAG)
          INTEGER N,IFLAG
          DOUBLE PRECISION X(N),FVEC(N)
          ----------
          CALCULATE THE FUNCTIONS AT X AND
          RETURN THIS VECTOR IN FVEC.
          ----------
          RETURN
          END

The variable IFLAG should not be changed, unless you want to stop the computation, in which case it should be set to a negative value.

But we want to pass the name of a Tcl procedure. So the task of the generator is:

The Tcl procedure could look like this:

 
    proc fcn {x} {
        ... Compute the values of each function and return as a list.
        ... If we have to decide the computation is not going well,
        ... return an error
    }

Now, the fexternal command will generate the source term to do this:

 
Wrapfort::fexternal fcn {
    fortran {
        integer      n      input
        double-array x      {input n}
        double-array fvec   {output n}
        integer      iflag  output
    }
    toproc {
        x      input
        fvec   result
    }
    onerror {
        iflag = -1;
    }
}

It is important to note that the name "fcn" given as the first argument must match the name of the external procedure given in the call to fproc. This is the way the various Fortran and C routines and functions "know" what to call.

As an aside: the interface for fexternal is different than that for fproc, because fexternal has to generate two very different interfaces. It is easier to distinguish what goes where in this way. Note that the variables x and fvec mentioned in the fortran and toproc sections are the same!

COMMANDS

The Wrapfort package defines the following commands:

::Wrapfort::fsource pkgname filename
Set the name of the C source file to be generated. Should be used before any other Wrapfort commands, as it opens the source file for writing.

string pkgname (in)
Name of the package (to be used in the "package require" command)

string filename (in)
Name of the C source file that will be written
::Wrapfort::fproc tclname fortname description
Create a wrapper function/Tcl command to a Fortran routine.

string tclname (in)
Name of the Tcl command to be created

string fortname (in)
Name of the Fortran routine to be wrapped

list description (in)
List of triples, each describing an aspect of the interface (see below for more details)
::Wrapfort::fexternal interface description
Create a wrapper routine for the given interface, so that a Tcl command can be used.

string interface
Name of the interface to be wrapped. It must be the name of the dummy routine argument in the Fortran routine, generated via fproc that will call this procedure. The call to fexternal must come first (to get proper C code).

list description
List of sections with triplets each decribing an aspect of the interface (see below for more details)
The description of the Fortran routine for the [fproc] command consists of a list of elements grouped in triplets: For the [fexternal] command the description is somewhat more involved, as it consists of several parts. Each part consists of a keyword and a list containing the detailed description of that part.

The keywords are:

The description of the fortran section consists of triplets again: The toproc section has a list of names and roles (input or result).

And the onerror section contains a fragment of C term that will be called if the Tcl procedure throws an error. This way the Fortran routine that is called can handle the error appropriately.

FORTRAN 95 ASPECTS

In order to successfully use the Wrapfort package, you need to know a few things about the interfacing between Fortran and C.

The easiest situation is that the Fortran interface uses only FORTRAN 77 features:

In that case there are only three things to worry about: If you want to wrap a Fortran routine that uses any of the Fortran 90/95 features that can not be used from C, then you will have to supply an interface routine written in Fortran that takes care of this. For instance:

 
    subroutine handle_array( array )
        real, dimension(:) :: array
        ...
    end subroutine

will have to wrapped via a second routine:

 
    subroutine handle_array_f77( n, array )
        integer            :: n
        real, dimension(n) :: array

        !
        ! The interface is needed because of the dimension(:)
        !
        interface
            subroutine handle_array( array )
                real, dimension(:) :: array
            end subroutine handle_array
        end interface

        call handle_array( array )
    end subroutine

This technique is also required, if the routine is contained in a module:

 
    subroutine handle_array_f77( n, array )

        use array_handling ! Contains handle_array

        integer            :: n
        real, dimension(n) :: array

        call handle_array( array )
    end subroutine

KEYWORDS

Fortran, generating code, interface, numerical libraries