Reputation: 11
I have been trying to debug this code which calls a Fortran subroutine, DLSODE. Two of the parameters of this subroutine are functions, FEX and JEX, which I am passing as function pointers. From what I understand, any of the functions used by a fortran subroutine must have call by reference parameters, as fortran does not accept call by value.
My problem is the error I get seems to indicate I have a discrepancy between the data types I declared in the function declaration of the pointer and the function definition below, and I have not been successful in debugging it.
#include <iostream>
#include <cmath>
using namespace std;
extern"C" {void DLSODE_(void (*fex)(int, double, double[], double[]), int *NEQ,
double *Y[], double *T, double *TOUT, int *ITOL,
double *RTOL, double *ATOL[], int *ITASK, int *ISTATE,
int *IOPT, double *RWORK[], int *LRW, int *IWORK[],
int *LIW, void (*jex)(int, double, double[], int,
int, double [3][3], int),
int *MF);
}
void FEX (int &NEQ, double &T, double Y[], int YDOT[]);
void JEX (int &NEQ, double &T, double Y[], int &ML, int &MU, double PD[3][3],
int &NRPD);
int main(){
int IOPT, IOUT, ISTATE, ITASK, ITOL, IWORK[23], LIW, LRW, MF, NEQ, ICOUNT=1;
double ATOL[3], RTOL, RWORK[58], T, TOUT, Y[3];
NEQ = 3;
Y[0] = 1;
Y[1] = 0;
Y[2] = 0;
T = 0;
TOUT = 0.4;
ITOL = 2;
RTOL = pow(10,-4);
ATOL[0] = pow(10,-6);
ATOL[1] = pow(10,-10);
ATOL[2] = pow(10,-6);
ITASK = 1;
ISTATE = 1;
IOPT = 0;
LRW = 58;
LIW = 23;
MF = 21;
for (ICOUNT; ICOUNT <13; ICOUNT++)
{
DLSODE_(&FEX, &NEQ, &Y, &T, &TOUT, &ITOL, &RTOL, &ATOL, &ITASK, &ISTATE,
&IOPT, &RWORK, &LRW, &IWORK, &LIW, &JEX, &MF);
cout<<"At t= "<<T<<" y= "<<Y[1]<<" "<<Y[2]<<" "<<Y[3]<<"\n";
}
}
void FEX (int &NEQ, double &T, double Y[], double YDOT[]){
YDOT[0] = -.04*Y[0] + pow(10,4)*Y[1]*Y[2];
YDOT[2] = 3*pow(10,7)*Y[1]*Y[1];
YDOT[1] = -YDOT[0] - YDOT[2];
}
void JEX (int &NEQ, double &T, double Y[], int &ML, int &MU, double &PD[3][3],
int &NRPD){
PD[0][0] = -.04;
PD[0][1] = 1*pow(10,4)*Y[2];
PD[0][2] = 1*pow(10,4)*Y[1];
PD[1][0] = .04;
PD[1][2] = -PD[0][2];
PD[2][1] = 6*pow(10,7)*Y[1];
PD[1][1] = -PD[0][1] - PD[2][1];
}
An the error message is
cversion1.cpp: In function ‘int main()’:
cversion1.cpp:44:52: error: invalid conversion from ‘void (*)(int&, double&, double*, int*)’ to ‘void (*)(int, double, double*, double*)’
cversion1.cpp:44:52: error: cannot convert ‘double (*)[3]’ to ‘double**’ for argument ‘3’ to ‘void DLSODE_(void (*)(int, double, double*, double*), int*, double**, double*, double*, int*, double*, double**, int*, int*, int*, double**, int*, int**, int*, void (*)(int, double, double*, int, int, double (*)[3], int), int*)’
cversion1.cpp: At global scope:
cversion1.cpp:55:77: error: declaration of ‘PD’ as array of references
cversion1.cpp:55:78: error: expected ‘)’ before ‘,’ token
cversion1.cpp:56:4: error: expected unqualified-id before ‘int’
Thank you,
Upvotes: 1
Views: 1538
Reputation: 29401
The modern way to interface C or C++ to Fortran is to use the Fortran ISO C Binding. This provides way to interface Fortran and C (and C++ via 'extern "C"') that is part of the Fortran language standard and is therefore compiler and platform independent. Statements such as "Fortran passes by reference" and that Fortran compilers append underscores (made in in the webpage linked in the other answer) are no longer necessary. The programmer can specify calling conventions compatible with C and the precise names. This is if you are allowed to modify the Fortran code. Or you can write a glue routine in Fortran between the C/C++ routine and the original Fortran routine. There are many previous questions/answers on Stackoverflow about the ISO C Binding. There are examples in the gfortran manual.
Upvotes: 4
Reputation: 5325
void (*fex)(int, double, double[], double[])
does not match
void FEX (int &NEQ, double &T, double Y[], double YDOT[])
You've declared argument 3 of DLSODE
to be double *Y[]
, an array of pointers to double
, but this should just be double Y[]
. You're passing in &Y
, and Y
is declared as double Y[3]
, but you should just pass in Y
.
In your JEX
declaration, you have double &PD[3][3]
, but you just want double PD[3][3]
here.
You should express calculations like 3*pow(10,7)
as 30000000
or 3E7
, which are compile-time constants.
Also keep in mind that C and C++ use row-major order for 2-d arrays, but FORTRAN uses column-major order. This will have the effect of transposing arrays when you pass them across the C/FORTRAN boundary.
That covers the error messages you've posted and all the errors I've seen.
Also, this seems like a good reference for what you're trying to do.
Upvotes: 3