Передача внутренней процедуры в качестве аргумента - PullRequest
4 голосов
/ 02 апреля 2012

Я хочу решить дифференциальное уравнение много раз для разных параметров. Это сложнее, чем это, но для ясности предположим, что ODE y'(x) = (y+a)*x с y(0) = 0 и я хочу y(1). Я выбрал алгоритм dverk из netlib для решения ODE, и он ожидает, что функция в правой части будет иметь определенную форму. Теперь то, что я сделал с компилятором Intel Fortran, выглядит следующим образом (упрощенно):

subroutine f(x,a,ans)
implicite none
double precision f,a,ans,y,tol,c(24),w(9)
...
call dverk(1,faux,x,y,1.d0,tol,ind,c,1,w)
...
contains
    subroutine faux(n,xx,yy,yprime)
           implicite none
           integer n
           double precision xx,yy(n),yprime(n)
           yprime(1) = (yy(1)+a)*xx
    end subroutine faux
end subroutine f

Это прекрасно работает с ifort, подпрограмма faux видит параметр a, и все работает как положено. Но я бы хотел, чтобы код был совместим с gfortran, и с этим компилятором я получаю следующее сообщение об ошибке:

Ошибка: внутренняя процедура 'faux' не допускается в качестве фактического аргумента в (1)

Мне нужно, чтобы внутри f была подпрограмма faux, иначе я не знаю, как определить значение a, потому что я не могу изменить список параметров, так как это то, что dverk рутина ожидает.

Я хотел бы сохранить процедуру dverk и понять, как решить эту конкретную проблему без обходного пути, поскольку я чувствую, что она снова станет важной, когда мне нужно будет интегрировать параметризованную функцию с различными интеграторами.

Ответы [ 3 ]

5 голосов
/ 03 апреля 2012

Вы можете поместить все это в модуль и сделать a глобальной переменной модуля.Сделайте faux модульной процедурой.Таким образом, он имеет доступ к a.

module ode_module
    double precision::a

    contains

    subroutine f(x,a,ans)
        implicit none
        double precision f,ans,y,tol,c(24),w(9)

        call dverk(1,faux,x,y,1.d0,tol,ind,c,1,w)

    end subroutine 

    subroutine faux(n,xx,yy,yprime)
       implicite none
       integer n
       double precision xx,yy(n),yprime(n)
       yprime(1) = (yy(1)+a)*xx
    end subroutine faux

end module
3 голосов
/ 04 апреля 2012

Здесь вы можете проверить, какие компиляторы поддерживают эту функцию F2008:

http://fortranwiki.org/fortran/show/Fortran+2008+status

На этой странице выполните поиск «Внутренняя процедура как фактический аргумент».

0 голосов
/ 03 апреля 2012

Прохождение внутренней процедуры разрешено Fortran 2008. Он не ограничивается модулем. Вы просто должны убедиться, что содержащаяся процедура все еще выполняется. Невозможно сохранить указатель и сделать так называемое «закрытие» известным из LISP и многих современных языков. В таких языках, как Fortran или C, окружающая среда для будущих вызовов не сохраняется, а указатели на внутренние функции становятся недействительными.

Преимущество внутренней процедуры перед процедурой модуля состоит в том, что вы можете делиться данными, не разделяя их во всем модуле.

Хорошо работает на достаточно старых версиях GCC (gfortran), Intel Fortran и Cray Fortran (лично проверено) и, возможно, других. Я помню, что не смог скомпилировать с помощью PGI и Oracle Fortran. Как показал Даниэль, его можно проверить на http://fortranwiki.org/fortran/show/Fortran+2008+status (поиск Внутренняя процедура в качестве фактического аргумента ). Самая последняя версия этой таблицы периодически публикуется на форуме ACM SIGPLAN Fortran.

В этой статье есть интересная информация http://software.intel.com/en-us/blogs/2009/09/02/doctor-fortran-in-think-thank-thunk/

...