在模块中使用外部函数

Using an external function in a module

我正在用 Fortran 编写一个 n 维数值求解器。我为此创建了一个从主程序调用的模块。我在编写一阶 ode 时使用 external 来调用未知函数。但是,在使用 external 复制多维结果时,出现以下错误

Error: EXTERNAL attribute conflicts with DIMENSION attribute in 'f_x' 

这意味着我的编译器无法处理带有数组输出的外部函数。

我尝试使用接口块来预定义具有固定尺寸的函数参数,但最终出现此错误

Error: PROCEDURE attribute conflicts with INTENT attribute in 'f_x' 

在 Fortran 中是否有任何方法可以让外部函数返回一个在子例程中使用虚拟函数初始化的数组。

这是我使用的代码。

module int_adaptive
implicit none
contains

subroutine RK4nd(f_x, norder, nvar, x_0, t_0, t_f, x_out, t_out)
    INTERFACE
        FUNCTION f_x (x_0, t_0)
            integer, parameter  :: dp=kind(0.d0)
            REAL(dp), INTENT(IN)    :: x_0(2, 3), t_0
            REAL, intent(out)   :: f_x(2,3)
        END FUNCTION f_x
    END INTERFACE


    integer, parameter  :: dp=kind(0.d0)
    integer             :: norder, i, nvar
    external            :: f_x
    real(dp), intent(in):: x_0(norder, nvar), t_0, t_f
    real(dp)            :: x_out(norder, nvar), t_out, k1(norder, nvar), y1(norder, nvar), y(norder, nvar)
    real(dp)            :: k2(norder, nvar), k3(norder, nvar), k4(norder, nvar),&
                           y2(norder, nvar), y3(norder, nvar), y4(norder, nvar)!, f_x(norder, nvar)
    real(dp)            :: h_min, h_0, h_max, tol, err_est, h, t

    if (h_0<h) then
        h = h_0
    end if
    if ((t_f - t_0) < 0.0) then
        h = -h
    end if
    t = t_0
    y = x_0

    do while (t .NE. t_f)
        k1 = f_x(y, t)
        y1 = y + k1*h/2.0

        k2 = f_x(y1, t+h/2.0)
        y2 = y + k2*h/2.0

        k3 = f_x(y2, t+h/2.0)
        y3 = y + k3*h

        k4 = f_x(y3, t+h)
        y4 = y + (k1+ 2*k2 + 2*k3 +k4)*h/6.0

        t = t + h
        y = y4

    end do

    x_out = y
    t_out = t

end subroutine
end module

我可以在模块内定义一个带有固定函数输入的标准积分器,并直接按名称调用它,但由于我处理各种阶数的 ODE,因此每次更改阶数时都会很复杂,我需要也更新此模块。

这里有两个截然不同的问题。

首先是您对函数 f_x 的显式接口的规范是 :函数结果 f_x 不能具有 intent(out) 属性。这涵盖了第二条错误消息。

第一条错误信息与后面使用

有关
external f_x

external 语句赋予 f_x external 属性。这很好,因为虚拟过程是一个外部函数。但是,您还为该过程提供了一个带有(现已更正的)接口块的显式接口。此接口块还声明该过程具有 external 属性。

这样一来,您就违反了一个约束条件,即不能在一个范围块中两次显式地为一个实体赋予相同的属性。要解决此问题,您应该删除其中一个规范。因为函数 returns 是一个数组结果,它的接口在引用它的子程序中必须是显式的,所以接口块必须保留。即去掉external语句。

为清楚起见,您还应删除注释掉的 real(dp) f_x(norder, nvar) 声明。这样的声明对于函数来说是错误的。

正如 francescalus 已经指出的那样,一个函数应该是明确的 INTERFACEd,或者声明为 EXTERNAL - 而不是两者。使用 EXTERNAL 是 old-fashion Fortran,因为函数接口是在 Fortran 90 中引入的,以取代对 EXTERNAL 的需要,这是一个相当过时的特性,对于向后兼容性仍然有效。

此外,函数结果不能具有 INTENT 属性。函数意味着 return "its own name" 的结果,如 y=f_x(...)。因此,您应该将 f_x 本身声明为 REAL(没有任何 INTENT 属性),或者更好的是,在函数声明及其接口中使用 RESULT 属性。在递归函数中使用 RESULT 属性实际上是必要的,但即使函数不是递归的,这样做也是一个好习惯。所以,你的函数的接口应该写成

INTERFACE
    FUNCTION f_x (x_0, t_0) RESULT(res)
        INTEGER, PARAMETER :: dp=kind(0.d0)
        REAL(kind=dp), DIMENSION(2,3), INTENT(IN) :: x_0
        REAL(kind=dp), INTENT(IN) :: t_0
        REAL, DIMENSION(2,3) :: res
    END FUNCTION f_x
END INTERFACE

在您实际的 f_x 实现中,您应该使用 res(或任何您想调用 RESULT 的东西)作为保存此函数应该 return.[= 的结果的变量19=]

请注意,我还做了一些并非绝对必要但强烈推荐的修改:像您一样声明 x_0REAL(dp), INTENT(IN) :: x_0(2, 3) 也是 old-fashion Fortran(DIMENSION 属性引入是为了让事情更清楚)。还有一些 Fortran 实现,尤其是 F-standard,不接受 REAL(dp),而是需要 REAL(kind=dp)