Python调用C/Fortran混合的动态链接库--中篇

接下来,介绍一个简单的例子,从fortran中传递并返回一维自定义结构体数组到python
注意点:
1、fortran新标准支持可分配数组作为变量传入并在subroutine或function分配后返回;
2、BIND不支持传入可分配数组(也即1和2无法同时使用);
3、fortran没有垃圾自动回收机制;
综合上述3点,利用ctypes调用fortran不能也不宜直接返回形状大小在计算前无法确定的数组,折衷的办法是:
a、估算出返回数组的大小,可以适当偏大;
b、在fortran的module定义全局可分配数组,第一次调用后存储结果并返回数组形状数值,在python中利用numpy开辟空数组,再次调用fortran得到结果并清空fortran中module的全局变量
fortran代码:

module sam
use,intrinsic::iso_c_binding
implicit none
type,bind(C)::abc
!Define struct
    character(len=80)::nam
    real(c_float)::val(10)
end type
! Global variable in fortran module
! Notice: dealloacte after using to avoid memory leak
type(abc),allocatable::stu(:)
contains
    subroutine calc(input,output,n1)bind(c,name='ex01')
    ! Example 01
    ! Both in and out variables has assumed shape
    ! Bind(C) not allowed pass alloctable variables
    ! Pure Fortran2008 allows pass alloctable array to subroutine to allocate or deallocate
    implicit none
    integer(c_int),intent(in),value::n1
    real(c_float),intent(in)::input(n1)
    type(abc),intent(out)::output(2*n1)
    
    integer::i

    do i=1,size(output)
        output(i)%nam = "abcdefea"
        output(i)%val(1:5) = real(i-1,4)
        output(i)%val(6::1) = i*2.5E0
    enddo    
    return
    end subroutine
    
    subroutine calc2(input,n1,n2)bind(c,name='ex02_1')
    ! Example 02_1
    ! Return result's shape
    implicit none
    integer(c_int),intent(in),value::n1
    real(c_float),intent(in)::input(n1)
    integer(c_int),intent(out)::n2

    integer::i,j

    call clear()
    if(input(1)<1.E0)then
        allocate(stu(10))
    else
        allocate(stu(int(input(1))+1))
    endif
    do i=1,size(stu)
        stu(i)%nam = "daefvdefefadfad"
        do j=1,size(stu(i)%val)
            stu(i)%val(j) = i*j*1.5E0
        enddo
    enddo
    n2 = size(stu)
    return
    end subroutine

    subroutine getdata(output,n)bind(c,name='ex02_2')
    ! Example 02_2
     ! Return result and do clear
    implicit none
    integer(c_int),intent(in),value::n
    type(abc),intent(out)::output(n)
    
    output = stu
    call clear()    
    return
    end subroutine

    subroutine clear()
    implicit none
    if(allocated(stu))deallocate(stu)
    end subroutine
end module

program test
use sam
implicit none
real(c_float)::array(5)
type(abc)::student(5*2)
integer::i

array = [5.E0,4.E0,6.E0,1.E0,7.E0]
call calc(array,student,5)
do i=1,size(student)
    write(*,*)student(i)%nam,student(i)%val
enddo
end program

 

python代码:

 1 #! /usr/bin/env python
 2 #coding=utf-8
 3 
 4 '''
 5 A short example of use ctypes and numpy to call fortran dynamic library
 6 example for 1d struct array pass or access
 7 '''
 8 import numpy as np
 9 from numpy.ctypeslib import load_library,ndpointer
10 from ctypes import c_int
11 
12 #define struct as same sa fortran
13 abc = np.dtype([("nam",'S80',1),("val",'f4',10)])
14 #load dynamic library
15 flib = load_library("libexample",".")
16 #function handle of ex01
17 fun01 = flib.ex01
18 #define input arguments
19 fun01.argtypes = [ndpointer('f4'),ndpointer(abc),c_int]
20 #
21 n1,n2 = 5,np.array(0,'i4')
22 vec = np.zeros((n1,),'f4')
23 student = np.empty((n1*2,),abc)
24 #print vec
25 fun01(vec,student,n1)
26 #print student
27 #function handle of ex02_1
28 fun02_1 = flib.ex02_1
29 fun02_1.argtypes = [ndpointer('f4'),c_int,ndpointer('i4')]
30 fun02_1(vec,n1,n2)
31 #print n1,n2
32 #function handle of ex02_2
33 fun02_2 = flib.ex02_2
34 fun02_2.argtypes = [ndpointer(abc),c_int]
35 student2 = np.empty((n2,),abc)
36 fun02_2(student2,n2)
37 print student2

 

编译命令:

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

 

posted @ 2014-09-15 14:09  pasuka  阅读(2133)  评论(0编辑  收藏  举报