gpt4 book ai didi

arrays - Julia 回调中的 Fortran 数组

转载 作者:行者123 更新时间:2023-12-01 01:56:03 24 4
gpt4 key购买 nike

我正在尝试为 twpbvpc(带重新网格化的 ODE BVP)Fortran-77 求解器编写一个包装器。求解器需要带签名的输入函数

subroutine fsub(ncomp, x, u, f, rpar, ipar)

在哪里
  • ncomp是一个整数(向量的长度),
  • x (in) 是一个浮点数,
  • u (in) 是长度为 ncomp 的向量,
  • f (out) 是结果的位置,长度为 ncomp 的向量
  • rparipar是浮点数和整数外部参数的数组; Julia 的关闭将是更受欢迎的方式,但显然它存在困难(请参阅 the blog post here )。但暂时可以忽略它们。

  • 在 Julia ,写 fsub我通常会使用签名
    function fsub_julia(x :: Float64, y :: Vector{Float64}, dy :: Vector{Float64})
    dy[1] = ...
    dy[2] = ...
    ...
    end
    ncomp似乎没有必要,因为可以通过 length 获得长度或 size (但是,Julia 可以检测从 Fortran 传递的数组的大小吗?对于测试代码,我明确知道 ncomp,所以现在这不是问题)。

    所以,为了符合 twpbvpc 格式,我写了一个包装器:
    function fsub_par(n :: Int64, x :: Float64, y :: Vector{Float64}, dy :: Vector{Float64}, rpar :: Vector{Float64}, ipar :: Vector{Float64})
    fsub_julia(x, y, dy)
    end

    现在,要将这个函数传递给 Fortran 例程,需要使用 cfunction 进行转换。声明类型。问题是如何?

    如果我把它写成:
    cf_fsub = cfunction(fsub_par, Void, (Ref{Int64}, Ref{Float64}, Ref{Float64}, Ref{Float64}, Ref{Float64}, Ref{Int64}))

    从 Fortran 调用时,出现错误:
    ERROR: LoadError: MethodError: no method matching (::TWPBVP.#fsub_par#1{TWPBVP_Test.#f})(::Int64, ::Float64, ::Float64, ::Float64, ::Float64, ::Int64)
    Closest candidates are:
    fsub_par(::Int64, ::Float64, !Matched::Array{Float64,1}, !Matched::Array{Float64,1}, !Matched::Array{Float64,1}, !Matched::Array{Float64,1})

    所以,不知何故,签名与数组参数不匹配......

    如果我更换 Ref{Float64}对于带有 Ref{Array{Float64,1}} 的数组参数(虽然看起来有点奇怪......):
    cf_fsub = cfunction(fsub_par, Void, (Ref{Int64}, Ref{Float64}, Ref{Array{Float64,1}}, Ref{Array{Float64,1}}, Ref{Array{Float64,1}}, Ref{Array{Int64,1}}))

    我在 fsub_par 时遇到段错误( cf_fsub ) 在 Fortran 代码中被调用(这大约位于,因为错误没有给出确切的位置)。

    更换 Ref{Float54}Ptr{Float64} for 数组也不做任何事情。

    我在 Fortran 代码中发现的一件有趣的事情是 fsub叫做:
    call fsub (ncomp, xx(1), u(1,1), fval(1,1),rpar,ipar)

    哪里 ufval声明为:
    dimension xx(nmsh), u(nudim,nmsh), fval(ncomp,nmsh)

    所以,我想,它使用了这样一个事实,即 Fortran 通过引用和引用来传递所有参数 u(1,1)应该是指向矩阵第一列的指针(据我所知,Fortran 和 Julia 以列优先顺序存储矩阵)。

    有什么出路?我需要更改 fsub_julia的签名吗?接受指针并将它们手动转换为数组(这就是 ODEInterface.jl 在较低级别的工作方式)?

    更新

    遵循在 ODEInterface.jl 中如何完成并结合在 void* 中传递 Julia 函数的想法C中的-thunk参数我想出了这个:
    immutable TWPBVPCProblem
    fsub :: Function # RHS function
    dfsub :: Function # Jacobian of RHS
    gsub :: Function # BC function
    dgsub :: Function # gradients of BC function
    end

    function unsafe_fsub(rn :: Ref{Int64}, rx :: Ref{Float64}, py :: Ptr{Float64}, pdy :: Ptr{Float64}, rpar :: Ptr{Float64}, ipar :: Ptr{Int64}) :: Void
    x = rx[]
    n = rn[]
    y = unsafe_wrap(Array, py, n)
    dy = unsafe_wrap(Array, pdy, n)
    problem = unsafe_pointer_to_objref(rpar) :: TWPBVPCProblem
    problem.fsub(x, y, dy)
    return nothing
    end

    const fsub_ptr = cfunction(unsafe_fsub, Void, (Ref{Int64}, Ref{Float64}, Ptr{Float64}, Ptr{Float64}, Ptr{Float64}, Ptr{Int64}))

    当我调用求解器时(它很长):
    function twpbvpc(nlbc :: Int64,
    aleft :: Float64, aright :: Float64,
    fixpnt :: Nullable{Vector{Float64}},
    ltol :: Vector{Int64}, tol :: Vector{Float64},
    linear :: Bool, givmsh :: Bool, giveu :: Bool, nmsh :: Ref{Int64},
    xx :: Vector{Float64}, u :: Array{Float64, 2}, nmax :: Ref{Int64},
    wrk :: Vector{Float64}, iwrk :: Vector{Int64},
    fsub :: Function, dfsub :: Function,
    gsub :: Function, dgsub :: Function,
    ckappa1 :: Ref{Float64}, gamma1 :: Ref{Float64},
    ckappa :: Ref{Float64},
    # rpar :: Vector{Float64},
    # ipar :: Vector{Int64},
    iflbvp :: Ref{Int64})

    # Keep problem functions in rpar
    # HACK!
    rpar = TWPBVPCProblem(fsub, dfsub, gsub, dgsub)

    # Fake external parameters
    # Can't have it 0-length as it would be Any[0] and not Float64[0]
    # local rpar :: Vector{Float64} = [0.0]
    local ipar :: Vector{Int64} = [0]

    # No need to pass these parameters
    # u is a matrix for the solution only!
    ncomp, nucol = size(u)
    # Get the maximum of xx
    nxxdim = length(xx)
    # max for mesh points must be the same as the number of column points of u
    assert(nucol == nxxdim)

    # Sizes of work arrays
    lwrkfl = length(wrk)
    lwrkin = length(iwrk)

    # Number of fixed mesh points
    if isnull(fixpnt)
    nfxpnt = 0
    fixpnt_v = [0.0]
    else
    fixpnt_v = get(fixpnt)
    nfxpnt = length(fixpnt_v)
    end

    # Size of tolerance vector ≤ ncomp
    ntol = length(ltol)

    ccall((:twpbvpc_, libtwpbvpc), Void,
    (Ref{Int64}, Ref{Int64}, # ncomp, nlbc,
    Ref{Float64}, Ref{Float64}, # aleft, aright
    Ref{Int64}, Ptr{Float64}, # nfxpnt, fixpnt
    Ref{Int64}, Ptr{Int64}, Ptr{Float64}, # ntol, ltol, tol
    Ref{Int64}, Ref{Int64}, Ref{Int64}, # linear, givmsh, giveu
    Ref{Int64}, Ref{Int64}, # nmsh, nxxdim
    Ptr{Float64}, Ref{Int64}, # xx, nudim
    Ptr{Float64}, Ref{Int64}, # u, nmax
    Ref{Int64}, Ptr{Float64}, # lwrkfl, wrk
    Ref{Int64}, Ptr{Int64}, # lwrkin, iwrk
    Ptr{Void}, Ptr{Void}, Ptr{Void}, Ptr{Void}, # fsub, dfsub, gsub, dgsub
    Ref{Float64}, Ref{Float64}, # ckappa1, gamma1
    Ref{Float64}, Any, Ptr{Int64}, # ckappa, rpar, ipar
    Ref{Int64}), # iflbvp
    ncomp, nlbc, aleft, aright,
    nfxpnt, fixpnt_v, ntol, ltol, tol,
    linear, givmsh, giveu, nmsh,
    nxxdim, xx, nucol, u, nmax, # nudim = nucol
    lwrkfl, wrk, lwrkin, iwrk,
    fsub_ptr, dfsub_ptr, gsub_ptr, dgsub_ptr,
    ckappa1,gamma1,ckappa,pointer_from_objref(rpar),ipar,iflbvp)
    end

    Fortran 的 twpbvpc看起来像这样(显然是开头):
      subroutine twpbvpc(ncomp, nlbc, aleft, aright,
    * nfxpnt, fixpnt, ntol, ltol, tol,
    * linear, givmsh, giveu, nmsh,
    * nxxdim, xx, nudim, u, nmax,
    * lwrkfl, wrk, lwrkin, iwrk,
    * fsub, dfsub, gsub, dgsub,
    * ckappa1,gamma1,ckappa,rpar,ipar,iflbvp)

    implicit double precision (a-h,o-z)
    dimension rpar(*),ipar(*)
    dimension fixpnt(*), ltol(*), tol(*)
    dimension xx(*), u(nudim,*)
    dimension wrk(lwrkfl), iwrk(lwrkin)

    logical linear, givmsh, giveu
    external fsub, dfsub, gsub, dgsub

    logical pdebug, use_c, comp_c
    common/algprs/ nminit, pdebug, iprint, idum, uval0, use_c, comp_c
    ...

    Fortran 代码使用 build.jl 编译:
    cd(joinpath(Pkg.dir("TWPBVP"), "deps"))
    pic = @windows ? "" : "-fPIC"
    run(`gfortran -m$WORD_SIZE -fdefault-real-8 -fdefault-integer-8 -ffixed-form $pic -shared -O3 -o libtwpbvpc.so twpbvpc.f`)

    所以,我路过 rparAny (应该等同于 Ptr{Void} ):尽管 Fortran 需要一个浮点数数组,但这应该无关紧要。

    现在,当我尝试运行一个简单的程序(在 Pkg.test("TWPBVP") 上)时出现段错误:
    signal (11): Segmentation fault
    while loading /home/alexey/.julia/v0.5/TWPBVP/test/runtests.jl, in expression starting on line 58
    unknown function (ip: 0xffffffffffffffff)
    Allocations: 1400208 (Pool: 1399373; Big: 835); GC: 0

    由于代码越来越长,这里是github上完整代码的链接: https://github.com/mobius-eng/TWPBVP.jl

    最佳答案

    Do I need to change the signature of fsub_julia to accept the pointers and convert them to arrays manually (this is how ODEInterface.jl works at the lower level)?



    是的,ODEInterface.jl 模型看起来很不错。

    您需要找出的第一件事是您的 fortran 的大小 INTEGER类型( Int32Int64 )。对于下面的代码,我将从 ODEInterface.jl 借用并使用 FInt (它可以是类型参数,也可以是 typealias )

    由此产生的回退应该类似于:
    # SUBROUTINE FSUB(NCOMP,X,Z,F,RPAR,IPAR)
    # IMPLICIT NONE
    # INTEGER NCOMP, IPAR
    # DOUBLE PRECISION F, Z, RPAR, X
    # DIMENSION Z(*),F(*)
    # DIMENSION RPAR(*), IPAR(*)
    function unsafe_fsub(ncomp::Ref{FInt}, x::Ref{Float64}, z::Ptr{Float64},
    f::Ptr{Float64}, rpar::Ptr{Float64}, ipar::Ptr{FInt})::Void
    xx = x[]
    zz = unsafe_wrap(Array, z, ncomp[])
    ff = unsafe_wrap(Array, f, ncomp[])
    fsub!(xx, zz, ff) # function which updates array ff
    return nothing
    end

    const fsub_ptr = cfunction(unsafe_fsub, Void,
    (Ref{FInt},Ref{Float64},Ptr{Float64},Ptr{Float64},Ptr{Float64},Ptr{FInt}))

    关于arrays - Julia 回调中的 Fortran 数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40482764/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com