Calling NAG Fortran Library Routines from C Language Programs
Using the NAG C Header Files
Ian Hounam
NAG Ltd, Oxford
© The Numerical Algorithms Group Ltd, Oxford UK. 1998
19 March 1998
Modified version for the Cray T3E
NSC, Linköping
31 October 2000
Please contact Bo Einarsson
if you experience any problem with these header files. The header files for Mark 19 are now available, see
section 14.
Some further information is available in the
NAG
C Header Files FAQ.
Please note that the Header Files discussed in sections 1 to 13 are for the
NAG Fortran Library
at Mark 18 and not for Mark 19.
They can be used for both marks, provided that the routine of
Mark 19 is not new to that mark.
Both marks are presently
available at NSC, you have to explicitly link with -lnag18
instead of with -lnag if you wish to use Mark 18
of the library, since Mark 19
is the default.
A header file containing the function prototypes can be included in the user's program to allow the C compiler to check argument passage. Such a header file has been created for the current NAG Fortran Library and the current NAG Graphics Library. This was done automatically from the Library source code in order to ensure its correctness. The T3E version nagmk18_t3e.h has however been modified manually from the original. This document explains how to call Fortran routines from C using the NAG header file. It is in most cases also possible to call the Fortran routines from C++.
This document describes how this is achieved on the Cray T3E. For all other computers you are referred to the original NAG version of this document.
In addition to the NAG produced header file we also present a locally produced header file with many advantages. See Section 13.
The two different header files are located on the area /usr/include/ on our Cray T3E, but this is a standard area for include files, and the directory does not have to given by the user.
REAL D double d;
INTEGER I int i;
LOGICAL L int l;
CHARACTER*n S char s[n];
COMPLEX Z struct {double re,im;} z;
All arguments are passed as pointers to a variable; this means that a
constant must first be assigned to a variable and then the address of
the variable passed.
As Fortran stores multi-dimension arrays in column major order whereas C stores in row major order, either
SUBROUTINE X00XXE(D1,S,D2)
where the arguments are declared
REAL D1,D2
CHARACTER*5 S
would, with an f77 compatible compiler, be called from C thus
X00XXE(&d1,s,&d2,length_of_s);
where the arguments are declared
double d1,d2;
char s[5];
int length_of_s = 5;
(length_of_s is the length of the string s)
If there is more than one string argument, further extra length arguments are appended, in order, to the end of the argument list.
Procedure arguments, i.e. function or subroutine names, are passed by address in the normal C manner.
The Fortran type COMPLEX is provided in the NAG header files by the typedef "Complex", which expands to "struct {double re,im;}"
e.g.
CHARACTER*10 FUNCTION F(I)
is called thus:
extern void F(char *,int,int *);
char c[10];
int clen = 10,i;
F(&c,clen,&i)
This method does not work on the Cray T3E, because of a conflict
between the Fortran CHARACTER and the C char.
I therefore recommend the Cray approach where char strings from C are explicitly translated into Fortran CHARACTER strings. The process is documented in the Cray C/C++ Reference Manual, 004-2179-003, Section 8.1.2, Calling Fortran functions and subroutines from a C or C++ function. See Example 2 in section 12.2 below.
Cray also suggests that LOGICAL variables are explicitly translated to 0 and 1 in a similar way. This is necessary on the C90, where .TRUE. is represented by a negative number, but on the T3E the logical representation is normal, so the NAG method works.
int array[] /* 2 dimension */
and for 3 dimensions:
double array[] /* 3 dimension */The prototype for a hypothetical NAG Fortran routine with a 2 dimensional DOUBLE PRECISION array argument would look like this:
extern void NAGSUB(double[] /* 2 dimensions */);A simple program to call this routine might look like this:
main ()
{
double p[2][2];
NAGSUB((double *)p);
}
Note that we need to cast the 2 dimensional C array actual argument to
(double *).
The example prototype below shows how to call a hypothetical NAG routine that takes a single subroutine argument. This user supplied subroutine takes a 2 dimension DOUBLE PRECISION array and an integer which specifies the leading dimension of the Fortran array.
extern void NAGSUB(void (*f) (double[] /* 2 dimension */, int *));
The C code for the user supplied function is listed below. The 2 dimension array is passed as a pointer to double and the code must carry out array indexing using the dimension information passed from Fortran. In this case, the macro P uses the leading dimension of the Fortran array, which is the trailing dimension of the C array, to index into the array p. The array p is only referenced through this macro.
void fun(double p[], int *tdp)
{
#define P(I,J) p[(I)*(*tdp) + (J)]
P(0,0) = 0.0;
P(1,1) = 1.0;
}
The main function looks like this:
main ()
{
void fun(double p[], int *tdp);
NAGSUB(fun);
}
Example 3 below shows a complete example program that illustrates
these concepts.
cc -c myprog.c f90 -o myprog myprog.o -L/usr/local/lib -lnagNote that the location of the library has to be given with the -L switch on the T3E.
If you use C++ it has been recommended by Anton Starikov to use compilation and linking with the same command:
CC -o myprog mprog.c -L/usr/local/lib -lnagThis worked fine with the examples 1 and 3 below. With separate compilation and linking it did not work at all:
CC -c nag1.c f90 nag1.o -L/usr/local/lib -lnag cld-404 cld: WARNING The symbol `_main' referenced in relocatable object `nag1.o:nag1$c' is not defined. cld-431 cld: WARNING The resulting output file `a.out' is not executable because of previous WARNING messages.Compilation with CC failed on example 2 since I do not use any header file in that case. At least those routines that are used explicitly have to be specified in some way.
Note that the values of eps are different from those in the NAG manual and the official NAG Fortran example.
#include <math.h>
#include <stdio.h>
#include <nagmk18_t3e.h>
main()
{
double eps, eta, x;
int ifail, k, nfmax;
double f(double *);
printf("C05AJF/E Example Program Results\n\n");
for (k=1; k<=2; k++)
{
eps = k==1 ? 0.1e-3 : 0.1e-4;
x = 1.0;
eta = 0.0;
nfmax = 200;
ifail = 1;
C05AJE(&x,&eps,&eta,f,&nfmax,&ifail);
if (ifail==0)
printf("With eps = %e root = %f\n",eps,x);
else
{
printf("ifail = %d\n",ifail);
if (ifail==3 | ifail==4)
printf("With eps = %e final value = %f\n",eps,x);
}
}
}
double f(double *x)
{
return exp(-*x) - *x;
}
Regrettably, I have not gotten this program to work with the NAG C Header Files, because of a conflict between the Fortran CHARACTER and the C char.
I have therefore instead used the Cray approach, documented in in the Cray C/C++ Reference Manual, 004-2179-003, Section 8.1.2, Calling Fortran functions and subroutines from a C or C++ function.
Also note that F01QDF/E is officially withdrawn from the present Mark 18 of the library, but the routines are still available.
The example is available in the file nag2.c. Note that the words "Transpose" and "Separate" are explicitly translated into Fortran CHARACTER strings with the Cray provided functions _cptofcd and strlen.
#include <stdio.h>
#include <fortran.h>
/* Simplified example program for F01QDF/E */
main()
{
int i,ifail,j,m = 5,n = 3,ncolb = 2;
int lda = m,ldb = m;
_fcd ftnstring1, ftnstring2;
/* Initialise arrays in column major order */
static double a[3][5] =
{
2.0, 2.0, 1.6, 2.0, 1.2,
2.5, 2.5, -0.4, -0.5, -0.3,
2.5, 2.5, 2.8, 0.5, -2.9
};
static double b[2][5] =
{
1.1, 0.9, 0.6, 0.0, -0.8,
0.0, 0.0, 1.32, 1.1, -0.26
};
double work[2],zeta[3];
printf("F01QDF/E Example Program Results\n\n");
ifail = 0;
F01QCE(&m,&n,(double *)a,&lda,zeta,&ifail);
ifail = 0;
ftnstring1 = _cptofcd("Transpose", strlen("Transpose"));
ftnstring2 = _cptofcd("Separate", strlen("Separate"));
F01QDE(ftnstring1,ftnstring2,&m,&n,(double *)a,&lda,zeta,&ncolb,
(double *)b,&ldb,work,&ifail);
printf("Matrix Q'*B\n");
for (i=0; i<m; i++)
{
for (j=0; j<ncolb; j++)
{
printf("%f ",b[j][i]);
}
printf("\n");
}
}
/* D03PCF/E Example Program
C Version. NAG Copyright 1997. */
#include <stdio.h>
#include <math.h>
/* Include the appropriate NAG C Header File */
#include <nagmk18_t3e.h>
/* Global data */
double alpha;
/* Routine for PDE initial condition */
void uinit(double u[20][2], double *x, int npts)
{
int i;
for (i=0; i<npts; i++)
{
u[i][0] = 2.0*alpha*x[i];
u[i][1] = 1.0;
}
}
/* Define the system of PDEs */
void pdedef(int *npde, double *t, double *x, double *u, double dudx[],
double p[], double q[], double r[], int *ires)
{
/* Macro to do subscripting into 2 dimensional array p, which
is passed as double p[]. */
#define P(I,J) p[(I)*(*npde) + (J)]
q[0] = 4.0*alpha*(u[1] + *x * dudx[1]);
q[1] = 0.0;
r[0] = *x * dudx[0];
r[1] = dudx[1] - u[0]*u[1];
P(0,0) = 0.0;
P(0,1) = 0.0;
P(1,0) = 0.0;
P(1,1) = 1.0 - (*x)*(*x);
}
void bndary(int *npde, double *t, double u[], double *ux, int *ibnd,
double beta[], double gamma[], int *ires)
{
if (*ibnd==0)
{
beta[0] = 0.0;
beta[1] = 1.0;
gamma[0] = u[0];
gamma[1] = -u[0]*u[1];
}
else
{
beta[0] = 1.0;
beta[1] = 0.0;
gamma[0] = -u[0];
gamma[1] = u[1];
}
}
main()
{
const int npde=2, npts=20, intpts=6, itype=1, neqn=40, niw=64, nw=1128;
const double acc = 1.0e-4;
double hx, pi, piby2, tout, ts;
const m = 1, itask = 1, itrace = 0;
int i, ifail, ind, it, j;
/* u[npts][npde], uout[itype][intpts][npde], w[nw], x[npts] */
double u[20][2], uout[1][6][2], w[1128], x[20];
/* xout[intpts] */
double xout[] = {0.0,0.4,0.6,0.8,0.9,1.0};
int iw[64];
void bndary(int *npde, double *t, double *u, double *ux, int *ibnd,
double *beta, double *gamma, int *ires);
void pdedef(int *npde, double *t, double *x, double *u, double *dudx,
double p[], double *q, double *r, int *ires);
void uinit(double u[20][2], double *x, int npts);
printf("D03PCF/E Example Program Results\n\n");
alpha = 1.0;
ind = 0;
/* Set spatial mesh points */
piby2 = 0.5*X01AAE(&pi);
hx = piby2/(npts-1);
x[0] = 0.0;
x[npts-1] = 1.0;
for (i=1; i<=npts-2; i++)
x[i] = sin(hx*i);
/* Set initial conditions */
ts = 0.0;
tout = 0.1e-4;
printf("Accuracy requirement = %lf\n",acc);
printf("Parameter alpha = %lf\n\n",alpha);
printf(" T / X ");
for (i=0; i<=5; i++)
printf("%8.4lf",xout[i]);
printf("\n");
/* Set the initial values */
uinit(u,x,npts);
for (i=0; i<=4; i++)
{
ifail = -1;
tout = 10.0*tout;
/* Cast 2-D array u to double * to match prototype */
D03PCE(&npde,&m,&ts,&tout,pdedef,bndary,(double *)u,&npts,x,&acc,w,
&nw,iw,&niw,&itask,&itrace,&ind,&ifail);
/* Interpolate at required spatial points */
/* Cast 2-D array u and 3-D array uout
to double * to match prototypes */
D03PZE(&npde,&m,(double *)u,&npts,x,xout,&intpts,&itype,
(double *)uout,&ifail);
printf("%6.4lf U(1)",tout);
for (j=0; j<intpts; j++)
printf("%8.4lf",uout[0][j][0]);
printf("\n U(2)");
for (j=0; j<intpts; j++)
printf("%8.4lf",uout[0][j][1]);
printf("\n");
}
printf("\n");
/* Print integration statistics */
printf("Number of integration steps in time %d\n",iw[0]);
printf("Number of residual evaluations of resulting ODE system %d\n",iw[1]);
printf("Number of Jacobian evaluations %d\n",iw[2]);
printf("Number of iterations of nonlinear solver %d\n",iw[4]);
}
The routine NFUE04 is missing from the NAG header file in Mark 18, therefore I had to write a special short header file for it, see the NFUE04 Header File. As a separate file it has to be referenced with the correct path. In the example it is assumed that the short header file is on the same directory as the example file.
The example worked fine with both the C compiler and the C++ compiler.
This program which does not use the NAG header files works fine with the C compiler but not with the C++ compiler.
This program which does not use the NAG header files works fine with the C compiler but not with the C++ compiler.
/*fortran char definition*/
typedef struct { char* s; int len; } FTCHAR;
#include <complex.h> /*fortran complex definition*/ #ifndef __cplusplus #define COMPLEX double complex #else #define COMPLEX complex #endif
I have run all the six examples above with this header file, and it works with both the C and the C++ compiler. Use the commands
cc -o myprog myprog.c -L/usr/local/lib -lnagor
CC -o myprog myprog.c -L/usr/local/lib -lnag
The examples have to be slightly modified, and are available as nag1_as.c, nag2_as.c, nag3_as.c, nag4_as.c, nag5_as.c, and nag6_as.c.
There are also two variants of the first example, in order to test the use of complex variables in C and C++, see nag1_as_cc.c for the C version, and nag1_as_CC.c for the C++ version.
#include <nagmk19_t3e.h>
This version is recommended for the NAG Library at Mark 19.
I have run all the six examples above with this header file, and it works with both the C and the C++ compiler. Use the commands
cc -o myprog myprog.c -L/usr/local/lib -lnagor
CC -o myprog myprog.c -L/usr/local/lib -lnag
The examples had to be slightly modified, and are available as nag1_19.c, nag2_19.c, nag3_19.c, nag4_19.c, nag5_19.c, and nag6_19.c.
There are also two variants of the first example, in order to test the use of complex variables in C and C++, see nag1_cc_19.c for the C version, and nag1_CC_19.c for the C++ version.