在R中调用带有可分配的FORTRAN子程序? [英] Call FORTRAN subroutine with allocatables in R?

查看:458
本文介绍了在R中调用带有可分配的FORTRAN子程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的后续问题以前的Fortran问题



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

pre $ 程序测试
整数,参数:: n = 3
整数,参数:: m = 4
双精度,维(n,m):: A
双精度,维(:, :),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)

包含

子程序extractB(A,B)
隐式无
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
(A,1)
if(A(i,2)== 56)然后
B(pos,1)= A(i,1)
B(pos,2 :) = A(i,3 :)
pos = pos + 1
end if
end do
结束子程序extractB
结束程序

程序编译,运行,并且它做得非常好。

我想用R调用 extractB 子例程。我问过类似的问题,发现能够使他们工作,但这一个是不同的,不起作用。 p>

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

 子程序extractB(A,B)
隐式无
双精度,维(:, :),意图(in):: A
双精度,维(:, :), 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
结束子程序extractB

我在R中编译它并加载t他用这些命令库:

  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,
X < - 。Fortran(extractB,A =未列表(A),B =数字(6))

之后,R崩溃

  ***发现段错误*** 
地址(无),导致'未知'

追踪:
1:.Fortran(extractB,A =未列表(A),B =数字(6))

可能的操作:
1:abort(如果启用了核心转储)
2:正常R退出
3:退出R而不保存工作区
4:退出R保存工作区
选择:

如果通过移除手动设置尺寸来更改子程序:

 子程序摘录(A,B)
隐含无
双精度,维(3,4) ,intent(in):: A
double precision,dimension(2,3):: B
integer :: i,pos
pos = 1
do i = 1,大小(A,1)
if(A(i,2)== 56)然后
B(pos,1)= A(i,1)
B(pos,2: )= A(i,3 :)
pos = pos + 1
end if
end do
end subroutine extract

重新编译库并重新加载它。我可以运行

X < - 。Fortran(extract,A = unlist(A),B = numeric(6))

dim(X $ A)< - dim(A)
dim(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

解决这个问题的方法有哪些?



非常感谢您的帮助!

解决方案

虽然弗拉基米尔F指出,这对于这样的allocatables不起作用,你可以分配内存,Fortran中的其他语言可以使用这些内存。但是在这种情况下你应该使用指针属性。但是,您将无法使用假定的形状数组。而不是推断A的大小,你应该明确地传递它。



如果您愿意为此更改接口,您可以使用这些接口(另请参阅MSBs对相关问题的回答):

<
使用iso_c_binding
隐式无
type(c,name = extractB)
> 子程序extractB(A_ptr,lenX,lenY,B_ptr,nrowB) 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,大小(A,2)-1))
!...
B_ptr = c_loc(B)
结束子程序extractB


This is a follow-up question to my previous Fortran question.

I have a working Fortran program that has a subroutine that filters an array. This is the program:

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

The program compiles, runs, and it does what it has to do very well.

I want to call the extractB subroutine with R. I have asked similar questions and found was able to make them work, but this one is somehow different and not working.

My fortran subrutine is in the mytest.f90 file and has this code:

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  

I compile it in R and load the library with these commands:

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

Then, in R, i create and pass a data frame to the subroutine

A = data.frame(c(11,15,54), c(22,56,56), c(43,65,32), c(55,63,78)) X<-.Fortran("extractB", A = unlist(A), B = numeric(6))

After that R crashes

 *** 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: 

If I change the subroutine by removing setting the dimensions by hand:

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

recompile the library, and reload it. I can run

X<-.Fortran("extract", A = unlist(A), B = numeric(6))

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

and get what I want

> 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

Any way of fixing this?

Thanks a lot for the help!

解决方案

Though, as Vladimir F pointed out, this will not work with allocatables like this, you can allocate memory, that can be used by another language in Fortran. But you should use the pointer attribute in that case. However, you will not be able to make use of assumed shape arrays either. Instead of inferring the size of A, you should pass it in explicitly.

If you are willing to change the interface for this, you can get your functionality with something along these lines, (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

这篇关于在R中调用带有可分配的FORTRAN子程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆