【问题标题】:Calling Fortran subroutines with array arguments from Julia使用 Julia 的数组参数调用 Fortran 子例程
【发布时间】:2017-02-11 23:20:37
【问题描述】:

我可以调用编译这个fortran代码'test.f90'

subroutine test(g,o)
double precision, intent(in):: g
double precision, intent(out):: o
o=g*g
end subroutine

gfortran -shared -fPIC test.f90 -o test.so

并为 Julia 创建这个包装函数 test.jl:

function test(s)
  res=Float64[1]
  ccall((:test_, "./test.so"), Ptr{Float64}, (Ptr{Float64}, Ptr{Float64}), &s,res);
  return res[1]
end

并使用寻找的输出运行这些命令:

julia> include("./test.jl")    
julia> test(3.4)
11.559999999999999

但我想返回一个数组而不是一个标量。我想我已经尝试了一切,包括在this 答案中使用 iso_c_binding。但是我尝试的一切都会像这样向我抛出错误:

ERROR: MethodError: `convert` has no method matching convert(::Type{Ptr{Array{Int32,2}}}, ::Array{Int32,2})
This may have arisen from a call to the constructor Ptr{Array{Int32,2}}(...),
since type constructors fall back to convert methods.
Closest candidates are:
  call{T}(::Type{T}, ::Any)
  convert{T}(::Type{Ptr{T}}, ::UInt64)
  convert{T}(::Type{Ptr{T}}, ::Int64)
  ...
 [inlined code] from ./deprecated.jl:417
 in unsafe_convert at ./no file:429496729

例如,我想从 julia 调用以下代码:

subroutine arr(array) ! or  arr(n,array)
implicit none
integer*8, intent(inout) :: array(:,:)
!integer*8, intent(in) :: n
!integer*8, intent(out) :: array(n,n)
integer :: i, j

do i=1,size(array,2) !n
    do j=1,size(array,1) !n
        array(i,j)= j+i
    enddo
enddo
end subroutine

使用注释掉的变体也是一种替代方法,因为从 julia 调用时,仅更改参数似乎没有用。

那么我如何使用来自 Julia 的数组调用 fortran 子例程?

【问题讨论】:

  • 您说您尝试过iso_c_binding,您的意思是实际上是bind(C) 还是您只是使用了iso_c_binding 模块?具体是通过什么方式?哪个确切的代码对应于问题中的错误消息?
  • integer*8 不符合标准,并且从未成为任何 ISO FORTRAN/Fortran 标准的一部分。您可以使用内部模块ISO_Fortran_env 中的命名常量INT64ISO_C_binding 中的C_INT64_Tselected_int_kind 以安全可移植的方式使用大整数。
  • 当 Fortran 过程是子例程时,为什么您对 _test 的 ccall 描述了一个返回指针的函数?
  • 而且你肯定在做任何与语言之间的互操作性相关的事情时不希望假设形状数组(:,:)。始终使用简单的(n,n)。至少在你学会如何使用数组描述符的内部之前。
  • 如果没有返回数组的 BIND C 函数。

标签: arrays fortran julia fortran90 gfortran


【解决方案1】:

当使用ccall 时,Julia 数组应该作为元素类型的指针传递,以及一个描述大小的额外参数。

你的例子test.f90应该是:

subroutine arr(n,array)
implicit none
integer*8, intent(in) :: n
integer*8, intent(out) :: array(n,n)
integer :: i, j

do i=1,size(array,2) !n
    do j=1,size(array,1) !n
        array(i,j)= j+i
    enddo
enddo
end subroutine

像以前一样编译

gfortran -shared -fPIC test.f90 -o test.so

然后在朱莉娅:

n = 10
X = zeros(Int64,n,n) # 8-byte integers
ccall((:arr_, "./test.so"), Void, (Ptr{Int64}, Ptr{Int64}), &n, X)

【讨论】:

  • 这很好用,谢谢!我认为我犯的最一致的错误是我主要尝试使用Ptr{Array{Int64,2}} 作为ccall 中的输出变量说明符和/或返回类型。
【解决方案2】:

建议: '&n' 将导致较新版本的无效语法错误。 而且,使用 Ref 比使用 Ptr 更安全。

你可以试试:

ccall((:arr_, "./test.so"), Cvoid, (Ref{Int64}, Ref{Int64}), Ref(n), X)

【讨论】:

  • 哪种方式更安全?那么兼容吗?
  • 这似乎是 Julia 1.1.0 调用 fortran 函数的最佳(唯一?)方式。
猜你喜欢
  • 2017-03-21
  • 2017-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-16
  • 1970-01-01
相关资源
最近更新 更多