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