1

I'm trying to call the GSL rountine CQUAD from Fortran. My idea was to write a .c subroutine that calls the gsl rountine and depends on a function and bounds. Two problems: I have only very little idea about c and fortrans iso_c_binding. My attempt is as follows:

A simple calling program (similar to M.S.B's post in No output from a Fortran program using the Gnu Scientific Library via a c wrapper ):

program test

   use iso_c_binding

   interface
      function  my_cquad (f,a,b)  bind(c)
         import
         real (kind=c_double) :: my_cquad

         interface
            function f(x) bind(c)
               import
               real(kind=c_double) :: f,x 
            end function
         end interface

         real (kind=c_double) :: a,b
      end function my_cquad
   end interface

   real (kind=c_double) :: y,a,b

   a=0. ; b=1.
   y=my_cquad(g,a,b)
   print*,y

   stop

contains

   function g(x) bind(C)
      real(kind=c_double) :: g,x
      g=sin(x)/x
      return
   end function g

end program test

The .c subroutine (basically taken from the example given by the author of CQUAD in https://scicomp.stackexchange.com/questions/4730/numerical-integration-handling-nans-c-fortran):

#include <stdio.h>
#include <gsl/gsl_integration.h>

double my_cquad ( double my_f() , double a , double b  )
{
    gsl_function f;
    gsl_integration_cquad_workspace *ws = NULL;
    double res, abserr;
    size_t neval;

    /* Prepare the function. */
    f.function = my_f;
    f.params = NULL;

    /* Initialize the workspace. */
    if ( ( ws = gsl_integration_cquad_workspace_alloc( 200 ) ) == NULL ) {
        printf( "call to gsl_integration_cquad_workspace_alloc failed.\n" );
        abort();
        }

    /* Call the integrator. */
    if ( gsl_integration_cquad( &f, a , b , 1.0e-10 , 1.0e-10 , ws , &res , &abserr , &neval ) != 0 ) {
        printf( "call to gsl_integration_cquad failed.\n" );
        abort();
        }

    /* Free the workspace. */
    gsl_integration_cquad_workspace_free( ws );

    /* Bye. */
    return res;
}

The .c subroutine alone seems to work fine. This can be tested with:

double g (double x)
{
return sin(x)/x;
}

int main () {
double y;
y=my_cquad(g,0.,1.);
printf("y:    %2.18f\n", y);

return 0;
}

But together with the .f90 calling program, at the moment it compiles but at runtime I get a segmentation fault that I don't quite get.

Additionally, it would of course be good to have some kind of wrapper that creates a c-type function depending on a fortran type function. I'm thinking about something like:

function f_to_c(f) bind(c)
   real(kind=c_double) :: f_to_c
   real(kind=8) :: f
   f_to_c=real(f,kind=c_double)
end function

But this desn't cover the dummy variables.

Thanks in advance and very sorry for the amount of code.

Community
  • 1
  • 1
PeMa
  • 1,559
  • 18
  • 44
  • 1
    Don't reinvent the wheel: use fgsl (http://www.lrz.de/services/software/mathematik/gsl/fortran/). It is complete (including cquad) and works great. – Vivian Miranda Nov 03 '14 at 00:50
  • 2
    @ViniciusMiranda It depens, but when one simple interface block is enough I go for it instead of installation of full packages. The same way I use my own very simple interface `pthread_create` and `pthread_join` instead of using the existing Fortran POSIX package, studying its installation and mantaining it on all the computers where I need my code to compile. – Vladimir F Героям слава Nov 03 '14 at 09:08
  • @ViniciusMiranda Thanks, this is surely what comes in mind first. But for yet not clear reasons `make check` failed for all routines when compiling fgsl. So far there is no answer by the author to my issue. And as Vladimir F said, for one routine I don't see a need to install a complete package. Additionlly I like to have this bit of more control, especially if the standard package has bugs. – PeMa Nov 03 '14 at 13:02
  • The function `my_f` is a function pointer, right? I'm a little bit confused about it's declaration as `double my_f()`. Why does it work like this? – pawel_winzig Jul 26 '18 at 14:21

1 Answers1

3

Beware, according to the Fortran standard internal functions shall not have the bind(C) attribute. I moved the function to a module.

The a and b must be passed by value to my_cquad and x to the integrated function:

module functions_to_integrate

   use iso_c_binding

contains
   function g(x) bind(C)
      real(kind=c_double) :: g
      real(kind=c_double), value :: x
      g = sin(x)/x
   end function g
end module

program test

   use iso_c_binding

   use functions_to_integrate

   interface
      function  my_cquad (f,a,b)  bind(c)
         import
         real (kind=c_double) :: my_cquad

         interface
            function f(x) bind(c)
               import
               real(kind=c_double) :: f
               real(kind=c_double), value :: x 
            end function
         end interface

         real (kind=c_double), value :: a,b
      end function my_cquad
   end interface

   real (kind=c_double) :: y,a,b

   a = 0 ; b = 1
   y = my_cquad(g,a,b)

   print *,y

end program test

test:

> gfortran my_cquad.c test_cquad.f90 -lgsl -lopenblas
> ./a.out 
  0.94608307036718275  

This is an example of a wrapper to a normal Fortran function (please do not use kind=8 for many reasons explained in another questions):

module functions_to_integrate
   use iso_fortran_env
   use iso_c_binding


   integer, parameter :: wp = real64
contains

    pure function g(x)
      real(kind=wp) :: g
      real(kind=wp), intent(in) :: x
      g = sin(x)/x
    end function g

    function g_to_c(x) bind(C)
      real(kind=c_double) :: g_to_c
      real(kind=c_double), value :: x
      g_to_c = real(g(x),kind=c_double)
    end function

end module

program test

    use iso_c_binding

    use functions_to_integrate

    interface
      function  my_cquad (f,a,b)  bind(c)
          import
          real (kind=c_double) :: my_cquad

          interface
            function f(x) bind(c)
                import
                real(kind=c_double) :: f
                real(kind=c_double), value :: x 
            end function
          end interface

          real (kind=c_double), value :: a,b
      end function my_cquad
    end interface

    real (kind=c_double) :: y,a,b

    a = 0 ; b = 1
    y = my_cquad(g_to_c,a,b)

    print *,y

end program test

P.S. I also deleted your stop and return statement before the end. Somehow it is always driving me mad, but that may be just my OCD. I was too used to see it in old programs coming from ancient times.

P.P.S: You may wish to see the FGSL interface package linked by Vincius Miranda http://www.lrz.de/services/software/mathematik/gsl/fortran/ . I knew about that one, but I tried mainly to point out the errors so that you can make similar interfaces yourself, where no ready-made package is available.

  • Thanks! But I have some questions. I guess `g(x)` generally doesn't need to be pure? Could you provide a link to the `kind=8` question? I usually also have `complex` variables in my programs. And with something like `wp=8`, I can easily change between the precisions (if needed) using e.g. `real(wp)`,complex(wp)`, `1._wp` etc. . Is this behaviour the same with `wp=real64` ? And is there something similar for the `c_...` variables? – PeMa Nov 03 '14 at 13:16
  • 1
    @PeMa I made it pure, because it was convenient there, but it doesn't have to be pure. You should understand the kind notation and why numbers as 4 and 8 are non-portable. Read http://stackoverflow.com/a/856243/721644 http://stackoverflow.com/a/10521611/721644 and http://stackoverflow.com/questions/3170239/fortran-integer4-vs-integer4-vs-integerkind-4/3170438 – Vladimir F Героям слава Nov 03 '14 at 13:24
  • 1
    @PeMa This one is also decent http://fortranwiki.org/fortran/show/Real+precision . Notice the `wp=real64`, you can easily change that to `wp=real32`. It is better than have to change 8 to 4 everywhere. – Vladimir F Героям слава Nov 03 '14 at 13:26