【问题标题】:Calling GSL routine CQUAD from Fortran从 Fortran 调用 GSL 例程 CQUAD
【发布时间】:2014-11-02 18:12:39
【问题描述】:

我正在尝试从 Fortran 调用 GSL 常规 CQUAD。我的想法是编写一个 .c 子例程,它调用 gsl 例程并依赖于函数和边界。两个问题:我对 c 和 fortrans iso_c_binding 知之甚少。我的尝试如下:

一个简单的调用程序(类似于MSB在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

.c子程序(基本上取自CQUAD作者在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;
}

单独的 .c 子例程似乎工作正常。这可以通过以下方式进行测试:

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;
}

但是与 .f90 调用程序一起,在它编译的那一刻,但在运行时我得到了一个我不太明白的分段错误。

此外,拥有某种根据 fortran 类型函数创建 c 类型函数的包装器当然会很好。我正在考虑类似的事情:

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

但这并不涵盖虚拟变量。

提前感谢,非常抱歉代码量。

【问题讨论】:

  • 不要重新发明轮子:使用 fgsl (lrz.de/services/software/mathematik/gsl/fortran)。它是完整的(包括 cquad)并且效果很好。
  • @ViniciusMiranda 它依赖,但是当一个简单的接口块就足够时,我会选择它而不是安装完整的包。同样的方式,我使用我自己的非常简单的接口pthread_createpthread_join,而不是使用现有的 Fortran POSIX 包,研究它的安装并在我需要我的代码编译的所有计算机上维护它。
  • @ViniciusMiranda 谢谢,这肯定是首先想到的。但是由于尚不清楚的原因make check 在编译 fgsl 时所有例程都失败了。到目前为止,作者还没有回答我的问题。正如 Vladimir F 所说,对于一个例程,我认为不需要安装完整的软件包。另外,我喜欢有更多的控制,特别是如果标准包有错误。
  • 函数my_f是一个函数指针,对吧?我对它的声明为double my_f() 有点困惑。为什么会这样?

标签: c fortran gsl


【解决方案1】:

注意,根据 Fortran 标准,内部函数不应具有 bind(C) 属性。我将函数移到了一个模块中。

ab 必须按值传递给 my_cquadx 到集成函数:

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

测试:

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

这是一个普通 Fortran 函数的包装示例(请不要使用kind=8,原因在其他问题中有解释):

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

附:我还在结尾之前删除了您的stopreturn 声明。不知何故,它总是让我发疯,但这可能只是我的强迫症。我太习惯在古代的旧程序中看到它了。

P.P.S:您可能希望看到由 Vincius Miranda http://www.lrz.de/services/software/mathematik/gsl/fortran/ 链接的 FGSL 接口包。我知道那个,但我主要是想指出错误,以便您可以自己制作类似的界面,没有现成的包可用。

【讨论】:

  • 谢谢!但我有一些问题。我猜g(x) 一般不需要很纯吧?您能否提供kind=8 问题的链接?我的程序中通常也有complex 变量。使用wp=8 之类的东西,我可以轻松地在精度之间进行更改(如果需要),例如使用real(wp),complex(wp), 1._wp`等。这种行为是否与 wp=real64 相同? c_... 变量是否有类似的东西?
  • @PeMa 我把它变成纯的,因为它在那里很方便,但它不一定是纯的。您应该了解这种表示法以及为什么数字 4 和 8 是不可移植的。阅读stackoverflow.com/a/856243/721644stackoverflow.com/a/10521611/721644stackoverflow.com/questions/3170239/…
  • @PeMa 这个也不错fortranwiki.org/fortran/show/Real+precision。注意wp=real64,您可以轻松地将其更改为wp=real32。总比到处都改 8 到 4 好。
猜你喜欢
  • 2014-01-14
  • 2012-01-02
  • 2016-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-14
  • 2019-07-08
相关资源
最近更新 更多