I\'m trying to link a fortran subroutine with c++, but can\'t quite figure out what exactly is wrong here: The fortran subroutine calls some functions eg. d1mach or xermsg, whic
The reason why the problem persists might be simply because your lib3j6j9j.a
does not include necessary files (such as d1mach). Actually, we can compile necessary files rather directly, so I will summarize the procedure below:
1) Download drc3jm.f
(which calculates 3j-symbols) and dependencies from the Netlib/Slatec page (here or here). Unpack the archive file to get Fortran files (*.f).
tar xvf netlibfiles.tgz
2) Remove d1mach.f
, i1mach.f
, and r1mach.f
(if any). Instead, download their alternative versions from Netlib/blas (*):
rm -f i1mach.f r1mach.f d1mach.f
wget http://www.netlib.org/blas/i1mach.f
wget http://www.netlib.org/blas/r1mach.f
wget http://www.netlib.org/blas/d1mach.f
3) Compile all *.f files
gfortran testf.f90 *.f
together with a main program testf.f90 (in free-format), e.g.,
program main
implicit none
integer, parameter :: N = 1000
double precision coef( N ), M2min, M2max, M2
integer ier
ier = 0 ; coef(:) = 0.0d0
call DRC3JM( 15.0d0, 30.0d0, 40.d0, 2.0d0, M2min, M2max, coef, N, ier )
print *, "M2min, M2max, ier = ", M2min, M2max, ier
M2 = 2.0d0
print "(a, f20.15)", "coef = ", coef( nint(M2 - M2min+1) ) !! -0.019081579799192
end
Then running the executable gives the desired result.
3-a) We can also make these *.f as a library and link with C++ codes, e.g., as follows:
gfortran -c *.f
ar rv mylib.a *.o
g++ testc.cpp mylib.a -lgfortran
with a main program (testc.cpp)
#include <cstdio>
extern "C"
double drc3jm_ (double*, double*, double*,
double*, double*, double*, double*, int*, int*);
int main()
{
double* coef;
double L1, L2, L3, M1, M2min, M2max, M2;
int ier, k, N = 1000;
coef = new double [ N ];
L1 = 15.0; L2 = 30.0; L3 = 40.0; M1 = 2.0;
drc3jm_ ( &L1, &L2, &L3,
&M1, &M2min, &M2max, coef, &N, &ier );
printf( "M2min, M2max, ierr = %10.5f%10.5f%d\n", M2min, M2max, ier );
M2 = 2.0;
k = (int)(M2 - M2min + 1.0e-3);
printf( "coef = %20.15f\n", coef[ k ] ); // -0.019081579799192
return 0;
}
We can see that the two programs give the same coefficient (-0.019081579799192) for
j1=15, j2=30, j3=40, m1=2, m2=2, m3=-4
You can also get the same result with an online tool, e.g., here.
But depending on cases, it may be simpler to use other libraries. One approach is to use the corresponding GSL routines (here) as
#include <cstdio>
extern "C"
double gsl_sf_coupling_3j (int two_ja, int two_jb, int two_jc,
int two_ma, int two_mb, int two_mc);
int main()
{
double coef;
coef = gsl_sf_coupling_3j( 30, 60, 80, 4, 4, -8 ); // -0.019081579799205
// NOTE: all j's and m's need to be doubled.
printf( "coef = %20.15f\n", coef );
return 0;
}
Here you need to link necessary GSL libraries (e.g., g++ test.cpp -lgsl
or g++ test.cpp /usr/lib64/libgsl.so.0 /usr/lib64/libgslcblas.so.0
etc).
Yet another approach is to use a latest program WIGXJPF (the related paper is here). I tried this a bit and it seems extremely easy to install (only one make
) and use. For example, enter the example/
directory and try gcc -I../inc csimple.c ../lib/libwigxjpf.a
.
According to the above paper, this program may offer some accuracy and performance advantage.
(*) For more details, please see the Netlib/FAQ page (thanks to @VladimirF in the comment). We could utilize the original d1mach.f etc in Slatec, but we need to modify them so as to obtain correct machine-dependent constants. The above BLAS versions of d1mach.f etc handle this automatically, so they are more convenient.