在R中使用allocatables调用FORTRAN子例程?

时间:2015-07-23 19:12:31

标签: r fortran

这是my previous Fortran question的后续问题。

我有一个有效的Fortran程序,它有一个过滤数组的子程序。这是该计划:

program test
    integer, parameter :: n = 3
    integer, parameter :: m = 4
    double precision, dimension(n,m) :: A
    double precision, dimension(:,:), allocatable :: B


    A(1,:) = [11,   22,   43,   55]
    A(2,:) = [15,   56,   65,   63]
    A(3,:) = [54,   56,   32,   78]

    print*, 'A :'
    print*, int(A)

    CALL extractB(A, B)
    print*, 'B'
    print*, int(B)

contains

    subroutine extractB(A, B) 
        implicit none
        double precision, dimension(:,:), intent(in) :: A
        double precision, dimension(:,:), allocatable :: B
        integer :: nrowB, i, pos
        nrowB = count( A(:,2)==56)
        allocate( B(nrowB, size(A,2)-1 ) )
        pos = 1
        do i = 1, size(A,1)
            if(A(i,2)==56)then
                B(pos,1) = A(i,1)
                B(pos,2:) = A(i,3:)
                pos = pos+1
            end if
        end do
    end subroutine extractB
end program

程序编译,运行,并且它完成了它必须做的非常好的事情。

我想用R调用extractB子例程。我已经问过similar questions并且发现它能够使它们工作,但是这个有点不同而且不起作用。

我的fortran subrutine位于mytest.f90文件中,并且有以下代码:

subroutine extractB(A, B)
implicit none
    double precision, dimension(:,:), intent(in) :: A
    double precision, dimension(:,:), allocatable :: B
    integer :: nrowB, i, pos
    nrowB = count( A(:,2)==56)
    allocate( B(nrowB, size(A,2)-1 ) )
    pos = 1
    do i = 1, size(A,1)
        if(A(i,2)==56)then
            B(pos,1) = A(i,1)
            B(pos,2:) = A(i,3:)
            pos = pos+1
        end if
    end do
end subroutine extractB  

我在R中编译它并使用以下命令加载库:

system("R CMD SHLIB ./Fortran/mytest.f90") 
dyn.load("./Fortran/mytest.so")

然后,在R中,我创建数据帧并将其传递给子例程

A = data.frame(c(11,15,54),c(22,56,56),c(43,45,32),c(54,63,78))    X< - .Fortran(“extractB”,A = unlist(A),B =数字(6))

之后R崩溃

 *** caught segfault ***
address (nil), cause 'unknown'

Traceback:
 1: .Fortran("extractB", A = unlist(A), B = numeric(6))

Possible actions:
1: abort (with core dump, if enabled)
2: normal R exit
3: exit R without saving workspace
4: exit R saving workspace
Selection: 

如果我通过手动删除尺寸来更改子程序:

subroutine extract(A, B)
implicit none
    double precision, dimension(3,4), intent(in) :: A
    double precision, dimension(2,3)             :: B
    integer :: i, pos
    pos = 1
    do i = 1, size(A,1)
        if(A(i,2)==56)then
            B(pos,1) = A(i,1)
            B(pos,2:) = A(i,3:)
            pos = pos+1
        end if
    end do
end subroutine extract

重新编译库,然后重新加载它。我可以跑

X< - .Fortran(“提取”,A =未列出(A),B =数字(6))

昏暗(X $ A)< - 昏暗(A) 昏暗(X $ B)< - c(2,3)

得到我想要的东西

> X
$A
     [,1] [,2] [,3] [,4]
[1,]   11   22   43   55
[2,]   15   56   65   63
[3,]   54   56   32   78

$B
     [,1] [,2] [,3]
[1,]   15   65   63
[2,]   54   32   78

有什么方法可以解决这个问题?

非常感谢您的帮助!

1 个答案:

答案 0 :(得分:0)

尽管正如Vladimir F所指出的那样,这对于像这样的allocatable不起作用,你可以分配内存,可以被Fortran中的另一种语言使用。但是在这种情况下你应该使用指针属性。但是,您也无法使用假定的形状数组。您应该明确地传递它,而不是推断A的大小。

如果您愿意为此更改界面,可以使用以下内容获取您的功能,(see also M. S. B.s answer to a related question):

subroutine extractB(A_ptr, lenX, lenY, B_ptr, nrowB) bind(c,name=extractB)
    use iso_c_binding
    implicit none
    type(c_ptr), value :: A_ptr
    integer(kind=c_int), value :: lenX, lenY
    type(c_ptr) :: B_ptr
    integer(kind=c_int) :: nrowB

    real(kind=c_double), pointer :: A(:,:)
    real(kind=c_double), pointer :: B(:,:)
    integer :: i, pos

    c_f_pointer(A_ptr, A, [lenX, lenY])
    nrowB = count( A(:,2)==56)
    allocate( B(nrowB, size(A,2)-1 ) )
    !...
    B_ptr = c_loc(B)
end subroutine extractB