1

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, which aren't defined in the fortran subroutine but called externally. When compiling, the error is "undefined reference to d1mach_"(or xermsg). I tried linking a library I think might contain the said functions (There seems to be a file called d1mach.o and xermsg.o inside the library) but the same error still persists. What might I be doing wrong?

extern"C" {
void drc3jm_(double *L1,double *L2,double *L3,double *M1,double *M2MIN,
    double *M2MAX,double *THRCOF,int *NDIM,int *IER);
}

This is the function I use to call the subroutine, and haven't used any new headers beside iostream

*DECK DRC3JM
      SUBROUTINE DRC3JM (L1, L2, L3, M1, M2MIN, M2MAX, THRCOF, NDIM,
     +   IER)
CALL XERMSG('SLATEC','DRC3JM','L1-ABS(M1) less than zero or '//
     +      'L1+ABS(M1) not integer.',IER,1)

This is the declaration of the fortran subroutine which calls the undeclared function xermsg.

I link the library using the -L/path/lib instruction but to no avail. The subroutine is to calculate a mathematical function and is part of the slatec codes.

Please let me know what other information you might need.

pyroscepter
  • 205
  • 1
  • 3
  • 9
  • You need to link with the Fortran run-time library. Try adding -lgfortran to your link command – antlersoft Jun 02 '16 at 20:46
  • @antlersoft I've done that already, I made .o files for both files, then compiled them using the -lgfortran and -L instructions. That results in this error, with or without the -L instruction. – pyroscepter Jun 02 '16 at 20:48
  • Can you share the full command lines you're using to compile and link? – Smeeheey Jun 02 '16 at 20:53
  • @Smeeheey I used the following instructions : gfortran -c drc3jm.f g++ -c test.cpp g++ test drc3jm.o test.o -lgfortran -L/usr/3j6j9j – pyroscepter Jun 02 '16 at 20:55
  • 1
    Have you tried UTFG? *"What and where are r1mach, d1mach, and i1mach?"* http://www.netlib.org/misc/faq.html#2.17 They come from BLAS. For `xermsg` again the first link from the web search. – Vladimir F Героям слава Jun 02 '16 at 22:01
  • @MarkPlotnick I tried using -l/usr/Documents/3j6j9j.a (that's where the file lib3j6j9j.a is stored) but it says library isn't found. Where am I going wrong? – pyroscepter Jun 03 '16 at 05:39
  • @VladimirF yeah I'm working on it, downloaded the BLAC as you suggested, and trying to link it. Will post as soon as I have updates. Thanks! – pyroscepter Jun 03 '16 at 07:03
  • @VladimirF So I downloaded the blas.3.6.0.tar.gz file from the link you suggested but it seems to be a collection of .f files, none of them having the name of d1mach or something similar. So, 1. How do I link these .f files with my current program, and 2. Will some of these have the d1mach function intrinsic to the file? I apologize for my unfamiliarity with fortran or linking files as I have no training in either. Thanks! – pyroscepter Jun 03 '16 at 07:16
  • You should use pre-compiled BLAS for your ooerating system. Usually included in Linux distribution. After that you link with `-lblas` – Vladimir F Героям слава Jun 03 '16 at 07:27
  • @VladimirF I compiled the BLAS library using the make file, and got the last instruction as ranlib blas_LINUX.a. Can I use this? Maybe using the same -l command? – pyroscepter Jun 03 '16 at 07:39
  • @MarkPlotnick I don't quite follow. I've manually downloaded a library in the Documents folder by the name lib3j6j9j.a, how is a pathname different from the directory in which the library is stored? – pyroscepter Jun 03 '16 at 07:40
  • @VladimirF No leads. Same error even after using either -lblas or the -l command :( "undefined reference to d1mach_" – pyroscepter Jun 03 '16 at 07:50
  • @MarkPlotnick even that returns "cannot find library...", Do I need to install the downloaded library? if so, how do I go about it? – pyroscepter Jun 03 '16 at 07:52
  • What happens if you try g++ test.o drc3jm.o /your/path/lib3j6j9j.a (i.e., no -l or -L)? Also, does "nm /your/path/lib3j6j9j.a" give d1mach.o and xermsg.o etc? – roygvib Jun 03 '16 at 09:22
  • If nothing works for some reason, we can modify drc3jm.f such that d1mach(2) is replaced by 1.0d300 and all xermsg() are commented out. Then we can compile drc3jm.f stand-alone and seems to give correct results. Also, to be combined with C or C++, [GSL for coupling coeff](https://www.gnu.org/software/gsl/manual/html_node/Coupling-Coefficients.html#Coupling-Coefficients) may be more straightforward to use... – roygvib Jun 03 '16 at 10:11
  • Sorry, I was wrong about `-l` being able to take a pathname to an archive as an argument. I've removed my comments suggesting that. Just use the archive pathname, as @roygvib said, or possibly `-L /usr/Documents -l 3j6j9j` – Mark Plotnick Jun 03 '16 at 14:07
  • @MarkPlotnick Hi thanks, and actually I'm not sure either why things will not go well in OP's case (possibly lib3j6j9j.a doesn't include d1mach etc...?). I will write some workaround for compiling everything from scratch then. – roygvib Jun 03 '16 at 17:57

1 Answers1

3

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.

roygvib
  • 7,218
  • 2
  • 19
  • 36
  • Wow this was amazingly detailed! I'd almost given up hope. Thanks so much @roygvib! I haven't reached the 2nd step yet but it seems like it's a solution to the - nan values I'm getting. Will update in the morning regarding the progress. – pyroscepter Jun 03 '16 at 19:43
  • This works perfectly! The second approach is exactly what I wanted (The one with c++ linking. Without downloading the alternative versions of the d1mach etc., the problem was that it gave me -nan as the result. Downloading the alternative versions seems to have fixed this.). Thanks for providing a very elaborate and idiot proof answer! – pyroscepter Jun 04 '16 at 09:12
  • Actually, this was the first time that I used the BLAS version of d1mach.f etc, which turned out to be very useful. (Because I didn't know it, I needed to manually modify them to run some AMOS code, see this [page](http://stackoverflow.com/questions/34082554/how-to-call-fortran-routines-from-c/34094408#34094408)... so updated it.) As for NaN, I guess it is because the old version of d1mach.f gives 0.0 as a "huge" value (unless we uncomment necessary lines). Anyway good to know that there is an easy solution :) Cheers – roygvib Jun 04 '16 at 12:50