3

I wish to call fortran routine cbesj.f from my C++ code and how do I achieve this?

Here are steps I have done:

  1. Download cbesj.f plus dependencies from netlib amos webpage, http://www.netlib.org/cgi-bin/netlibfiles.pl?filename=/amos/cbesj.f

  2. In the source dir,

    f2c -C++PR *.f

    g++ -c *.c

    ar cr libmydemo.a *.o

  3. [test_cbesj.cpp][1] and [mydemo.h][2] are used to call the subroutine in this way,

    g++ test_cbesj.cpp -lf2c -lm -L. -lmydemo it returns bug:

test_cbesj.cpp:(.text+0xd6): undefined reference to `cbesj_(complex*, float*, long*, long*, complex*, long*, long*)'

What shall be the proper way to refer to cbesj_ subroutine in my problem? Thanks!

Thanks for casey: I think your approach is the best. But I still have set fault, why? Here we go:

f77 -c *.f    

in modemo.h

//File mydemo.h                                                                                                                              
#ifndef MYDEMO_H
#define MYDEMO_H
#include <stdio.h>      /* Standard Library of Input and Output */
#include "f2c.h"

extern"C" int cacai_(complex *z__, real *fnu, integer *kode, integer *mr,         integer *n, complex *y, integer *nz, real *rl, real *tol, real *el\
im, real *alim);
extern"C" int cairy_(complex *z__, integer *id, integer *kode, complex *ai,         integer *nz, integer *ierr);
extern"C" int casyi_(complex *z__, real *fnu, integer *kode, integer *n,     complex *y, integer *nz, real *rl, real *tol, real *elim, real     \
  *alim);
extern"C" int cbesj_(complex *z__, real *fnu, integer *kode, integer *n,     complex *cy, integer *nz, integer *ierr);
extern"C" int cbinu_(complex *z__, real *fnu, integer *kode, integer *n,     complex *cy, integer *nz, real *rl, real *fnul, real *tol, real *el\
im, real *alim);
extern"C" int cbknu_(complex *z__, real *fnu, integer *kode, integer *n, complex *y, integer *nz, real *tol, real *elim, real *alim);
extern"C" int cbuni_(complex *z__, real *fnu, integer *kode, integer *n,     complex *y, integer *nz, integer *nui, integer *nlast, real *fnul, \
real *tol, real *elim, real *alim);
extern"C" int ckscl_(complex *zr, real *fnu, integer *n, complex *y, integer *nz, complex *rz, real *ascle, real *tol, real *elim);
extern"C" int cmlri_(complex *z__, real *fnu, integer *kode, integer *n,     complex *y, integer *nz, real *tol);
extern"C" int crati_(complex *z__, real *fnu, integer *n, complex *cy, real *tol);
extern"C" int cs1s2_(complex *zr, complex *s1, complex *s2, integer *nz, real *ascle, real *alim, integer *iuf);
extern"C" int cseri_(complex *z__, real *fnu, integer *kode, integer *n, complex *y, integer *nz, real *tol, real *elim, real *alim);
extern"C" int cshch_(complex *z__, complex *csh, complex *cch);
extern"C" int cuchk_(complex *y, integer *nz, real *ascle, real *tol);
extern"C" int cunhj_(complex *z__, real *fnu, integer *ipmtr, real *tol, complex *phi, complex *arg, complex *zeta1, complex *zeta2, complex\
 *asum, complex *bsum);
extern"C" int cuni1_(complex *z__, real *fnu, integer *kode, integer *n, complex *y, integer *nz, integer *nlast, real *fnul, real *tol     \
  , real *elim, real *alim);
extern"C" int cuni2_(complex *z__, real *fnu, integer *kode, integer *n, complex *y, integer *nz, integer *nlast, real *fnul, real *tol     \
  , real *elim, real *alim);
extern"C" int cunik_(complex *zr, real *fnu, integer *ikflg, integer *ipmtr, real *tol, integer *init, complex *phi, complex *zeta1, complex\
 *zeta2, complex *sum, complex *cwrk);
extern"C" int cuoik_(complex *z__, real *fnu, integer *kode, integer *ikflg,     integer *n, complex *y, integer *nuf, real *tol, real *elim, re\
al *alim);
extern"C" int cwrsk_(complex *zr, real *fnu, integer *kode, integer *n,     complex *y, integer *nz, complex *cw, real *tol, real *elim, real *a\
lim);
extern"C" real gamln_(real *z__, integer *ierr);
extern"C" integer i1mach_(integer *i__);
extern"C" real r1mach_(integer *i__);
extern"C" int xerror_(char *mess, integer *nmess, integer *l1, integer *l2,     ftnlen mess_len);
    #endif

in test_cbesj.cpp,

#include "mydemo.h"
#include "f2c.h"
#include <math.h>
#include <iostream>
#include <stdio.h>
#include <assert.h>
using namespace std;
int main(void)
{
  //  double x=86840.;                                                                                                                       
  //int nu=46431,j, err;                                                                                                                     
  complex *z,z__;
  z__.r = 3.0;z__.i = 2.0;z = &z__;
  cout << z->r << '\t' << z->i << endl;
  real *fnu;float fnu__ = 3.0;fnu = &fnu__;
  integer *kode ;long int kode__=1;kode=&kode__;
  integer *n    ;long int n__=1;n=&n__;
  complex *cy;
  integer *nz;
  integer *ierr;
  cbesj_(z, fnu, kode, n, cy, nz, ierr);
  cout << cy->r << '\t' << cy->i << endl;

  return 0;
}

Then,

g++ -c -g test_cbesj.cpp
g++ -o test *.o -lg2c
./test
3   2
Segmentation fault (core dumped)
gdb test 
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /media/Downloads/amos-4/test...done.
(gdb) run
Starting program: /media/Downloads/amos-4/test 
3   2

Program received signal SIGSEGV, Segmentation fault.
0x0804b355 in cbesj_ ()
(gdb) frame 0
#0  0x0804b355 in cbesj_ ()
(gdb) frame 1
#1  0x0805a3ca in main () at test_cbesj.cpp:21
21    cbesj_(z, fnu, kode, n, cy, nz, ierr);

Thanks for roygvib's reply! Good suggestions actually. Here is the changed test_cbesj.cpp:

complex z, cy;
  float fnu;
  long int kode, n, nz, ierr;

  z.r = 3.0; z.i = 2.0;
  fnu = 3.0;
  n = 1; kode = 1;
  cout.precision(16);
  cbesj_( &z, &fnu, &kode, &n, &cy, &nz, &ierr );
  cout << cy.r << '\t' << cy.i << endl;
  cout << "nz=" << nz << endl;
  cout << "ierr=" << ierr << Lendl;

No more seg fault. But out of some reasons, the code does not work as expected:

./test
-1.343533039093018  -1.343533992767334
nz=0
ierr=4

and answers are wrong and ierr also says so from the source code:

C           NZ     - NUMBER OF COMPONENTS SET TO ZERO DUE TO UNDERFLOW,
C                    NZ= 0   , NORMAL RETURN
C                    NZ.GT.0 , LAST NZ COMPONENTS OF CY SET TO ZERO
C                              DUE TO UNDERFLOW, CY(I)=CMPLX(0.0,0.0),
C                              I = N-NZ+1,...,N
C           IERR   - ERROR FLAG
C                    IERR=0, NORMAL RETURN - COMPUTATION COMPLETED
C                    IERR=1, INPUT ERROR   - NO COMPUTATION
C                    IERR=2, OVERFLOW      - NO COMPUTATION, AIMAG(Z)
C                            TOO LARGE ON KODE=1
C                    IERR=3, CABS(Z) OR FNU+N-1 LARGE - COMPUTATION DONE
C                            BUT LOSSES OF SIGNIFCANCE BY ARGUMENT
C                            REDUCTION PRODUCE LESS THAN HALF OF MACHINE
C                            ACCURACY
C                    IERR=4, CABS(Z) OR FNU+N-1 TOO LARGE - NO COMPUTA-
C                            TION BECAUSE OF COMPLETE LOSSES OF SIGNIFI-
C                            CANCE BY ARGUMENT REDUCTION
C                    IERR=5, ERROR              - NO COMPUTATION,
C                            ALGORITHM TERMINATION CONDITION NOT MET
Franklin Dong
  • 183
  • 1
  • 1
  • 10
  • Did you check whether the function is defined in any of the C files produced by `f2c`? – R Sahu Dec 04 '15 at 07:01
  • 1
    Also, please post the contents of the files that you linked to. Links to source code are strongly discouraged here. It is worse when the links point to images. – R Sahu Dec 04 '15 at 07:03
  • 1
    At the very least, you will have to make your header declare the function as `extern C` so the function name it's looking for isn't mangled. – Anon Mail Dec 04 '15 at 07:08
  • 3
    Search Qs and As on the topic of *fortran interoperability with c*, learn how to call the Fortran routine from C++ without passing it through f2c. – High Performance Mark Dec 04 '15 at 08:16
  • 1
    made the Fortran interoperable. Compiling with f2c is almost guaranteed negatively impact the performance of the code. Use a real Fortran compiler and link everything together. Search for iso_c_binding, there are plenty of examples here. – casey Dec 04 '15 at 14:42
  • Thank you guys! Especially casey. But I still have seg fault now and not sure how to proceed. Please advise! cheers. – Franklin Dong Dec 04 '15 at 17:33
  • Thank you roygvib, seg fault is gone but fortran routine returns error flag 4 – Franklin Dong Dec 04 '15 at 18:36
  • @FranklinDong Did you possibly download files from http://netlib.org/amos/ ? I'm now trying both cbesj and zbesj with gfortran, but having troubles... (cbesj gives ierr=4, while zbesj cannot be compiled because of zabs()). If I use codes from a different site (e.g. openspecfun), everything works fine. – roygvib Dec 04 '15 at 19:27
  • @roygvib, yes yes and yes. I downloaded the files from netlib.org/amos and thought it should work just as fine. and cbesj gives ierr=4 only except nu=0, Z=(0.0,0.0) case. I guess the value is not passed in correctly. Can you please give a link to openspecfun? Thanks. – Franklin Dong Dec 04 '15 at 20:02
  • Are these in any way related to the C functions j0, j1, jn, y0, y1, yn? – cup Dec 23 '15 at 22:24

1 Answers1

3

I downloaded cbesj (or zbesj) and related files from netlib, but for some reason they did not work with gfortran4.8 (on Linux x86_64). More precisely, cbesj always gives ierr=4, while I could not compile zbesj because zabs has too many arguments (but please see the update below).


So I gave up using the original AMOS codes and tried openspecfun that is also based on AMOS. I compiled the latter simply by typing make (with gfortran), which generated libopenspecfun.a etc. Then I made the following code to test zbesj:

#include <cstdio>

extern "C" {
    void zbesj_( double *zr, double *zi, double *fnu, int *kode, int *n,
                 double *Jr, double *Ji, int *nz, int *ierr );
}

int main()
{
    int    n, nz, ierr, kode;
    double fnu, zr, zi, Jr, Ji;

    fnu = 2.5;
    zr = 1.0; zi = 2.0;
    n = 1; kode = 1;

    zbesj_( &zr, &zi, &fnu, &kode, &n, &Jr, &Ji, &nz, &ierr );

    printf( "fnu = %20.15f\n", fnu );
    printf( "z   = %20.15f %20.15f\n", zr, zi );
    printf( "J   = %20.15f %20.15f\n", Jr, Ji );
    printf( "nz, ierr = %d %d\n", nz, ierr );

    return 0;
}

and compiled as

g++ test_zbesj.cpp libopenspecfun.a -lgfortran

which gives

fnu =    2.500000000000000
z   =    1.000000000000000    2.000000000000000
J   =   -0.394517225893988    0.297628229902939
nz, ierr = 0 0

Because this site says the answer is -0.394517...+ 0.297628...i, the result of zbesj seems correct.


[Update]

By reading the original code more carefully and also comparing with openspecfun, it was found that the user needs to uncomment appropriate sections of i1mach.f and r1mach.f (or d1mach.f) depending on the machine environment. For Linux x86_64, it seems sufficient to uncomment the section tagged with

C     MACHINE CONSTANTS FOR THE IBM PC

With this modification, the cbesj worked correctly with ierr=0; otherwise it gives ierr=4 because some constants default to 0. For the double-precision version, we also need to deal with the user-defined ZABS, whose definition conflicts with the intrinsic one. The Intel Fortran (ifort) recognizes the user-defined ZABS as such and compiles them successfully (although with a lot of warnings), while gfortran stops compiling with syntax error (assuming it to be the intrinsic one). openspecfunc avoids this problem by rewriting all the ZABS with the intrinsic one. Anyway, with the above modifications both cbesj and zbesj worked correctly (as expected).


[Update 2]

It turned out that using the BLAS version of d1mach.f, r1mach.f, and i1mach.f, things become even simpler because they determine machine-dependent constants automatically and we don't need to modify the codes manually. Please see the Netlib/FAQ page for details. The same trick applies to SLATEC also (e.g., this page).

wget http://www.netlib.org/blas/i1mach.f 
wget http://www.netlib.org/blas/r1mach.f
wget http://www.netlib.org/blas/d1mach.f 
Community
  • 1
  • 1
roygvib
  • 7,218
  • 2
  • 19
  • 36
  • hi roygvib, it really works like a charm and I have just tested it worked as expected for very high orders, which solved my original question at http://stackoverflow.com/questions/34054482/high-order-bessel-function-computation-with-large-variables?noredirect=1#comment55866960_34054482. (although the ierr returns value 3, but it really does the job for me! ) I really really appreciate your time and help on this issue and thank you! It's really a shame netlib.org/amos does not have a bug free source code (up-to-date). – Franklin Dong Dec 04 '15 at 20:50
  • g++ test_zbesj.cpp -libopenspecfun.a -lgfortran does not work for me actually. But g++ test_zbesj.cpp -lopenspecfun -lgfortran worked after I edited ~/.bashrc with a line export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH and source ~/.bashrc – Franklin Dong Dec 04 '15 at 20:52
  • I'm still trying to find the reason why `cbesj` does not work for me, but not in success yet... It might be because of old fortran syntax and related inconsistency or something like that. Anyway, I believe it is better to use the double-precision version because special functions with complex arguments can generate very large values. – roygvib Dec 04 '15 at 20:56
  • Hi @FranklinDong I have read the code more carefully, and it seems that we need to uncomment some section of the machine-dependent constants by hands before compilation (see Update above). So this is not a "bug" and it may be better to update your another post (so that it will not imply the existence of bugs...) But it is sure that the code is not up-to-date (it is written more than 30 years ago!!) – roygvib Dec 05 '15 at 20:59
  • hi @roygvib, Thanks for getting back. I have also made a diff between these two projects of source codes and found out the ZABS diff made in openspecfun. And, I will check cbesj on different machines with different compilers too, with the comment you have updated. – Franklin Dong Dec 06 '15 at 22:11