使用ctypes在Python中调用Fortran共享库

时间:2016-02-26 13:01:35

标签: python fortran ctypes

我正在尝试在Python中使用Fortran模块。我在Python中有几个数组 - 包括数字和字符串。我在Fortran和Python中定义了数组,但我认为我对数字数组使用了错误的类型。我得到一个错误,转换第一个参数(数字数组)失败。我应该使用哪些类型?

错误:

Traceback (most recent call last):
  File "py_try.py", line 66, in <module>
    writelittler.write_obs(p,z,t,td,spd,wdir,slp,ter,xlat,xlon,date_char,num_met,num_lev,kx,dd_strvar,station_strvar,synop,string4, bogus, iseq_num, iunit)
ctypes.ArgumentError: argument 1: <type 'exceptions.TypeError'>: Don't know how to convert parameter 1

的Fortran:

subroutine write_obs(p, z, t, td, spd, wdir, xlon, kx, slp, ter, xlat,  date_char, dd, station, num_met, num_lev, synop, string4, bogus, iseq_num, iunit) bind(C, name='write_obs')
use iso_c_binding
implicit none
integer(c_int) k, kx, num_met, num_lev, iseq_num, iunit, ierr
real(kind=c_float) p(kx), slp(kx), z(kx), t(kx), td(kx)
real(kind=c_float) spd(kx), ter(kx), xlat(kx), xlon(kx), wdir(kx)
character(len=1,kind=c_char) date_char
character(len=1,kind=c_char) synop, string4
character(len=1,kind=c_char), intent(in) :: dd(kx)
character(len=1,kind=c_char), intent(in) :: station(kx)
logical(c_bool) bogus
character(len=84,kind=c_char) rpt_format
character(len=22,kind=c_char) meas_format
character(len=14,kind=c_char) end_format

rpt_format = ' ( 2f20.5 , 2a40 , '&
&' 2a40 , 1f20.5 , 5i10 , 3L10 , '&
&' 2i10 , a20 , 13( f13.5 , i7 ) )'

meas_format = ' ( 10( f13.5 , i7 ) ) '
end_format = ' ( 3 ( i7 ) )'

do 100 k=1 , kx

write ( UNIT = iunit , iostat = ierr , FMT = rpt_format ) &
& xlat(k), xlon(k), dd(k), station(k), &
& synop , string4, ter(k), num_met, 0, 0, iseq_num, 0, &
& .true., bogus, .false., &
& -888888, -888888, date_char, slp(k), 0, &
& -888888., 0, -888888., 0, -888888., 0, &
& -888888., 0, -888888., 0, -888888., 0, -888888., 0, &
& -888888., 0, -888888., 0, -888888., 0, -888888., 0, &
& -888888., 0

write ( UNIT = iunit , iostat = ierr , FMT = meas_format ) &
& p(k), 0, z(k), 0, t(k), 0, td(k), 0, &
& spd(k), 0, wdir(k), 0, &
& -888888., 0, -888888., 0, -888888., 0, -888888., 0


write ( UNIT = iunit , iostat = ierr, FMT = meas_format ) &
& -777777., 0, -777777., 0, float(num_lev), 0, &
& -888888., 0, -888888., 0, -888888., 0, &
& -888888., 0, -888888., 0, -888888., 0, &
& -888888., 0

write ( UNIT = iunit, iostat = ierr, FMT = end_format ) &
& num_lev, 0, 0

if (ierr .NE. 0 ) then
    print '(A,I5,A)','Troubles writing a sounding.Error #', ierr
    stop 'writing_error'
endif
100 continue
return
end subroutine write_obs

的Python:

import numpy as np
import ctypes
from ctypes import c_int, c_char
writelittler=ctypes.CDLL("/writelittler.so")
p = np.asarray([ 982.6, 999.7 ], dtype="float64")
p_strvar=ctypes.c_void_p(p.ctypes.data)

# [other numerical arrays omitted]

bogus = 0
kx=2
iseq_num = 0
iunit=2
date_char = '      20160128060000'
dt=np.dtype('a40')
dd = np.asarray([ '1111111111111111111111111111111111111111', '6666666666666666666666666666666666666666', '9999999999999999999999999999999999999999'  ], dtype=dt)
station = np.asarray([ 'V111111111111111111111111111111111111111','M111111111111111111111111111111111111111' ], dtype=dt)

dd_strvar=ctypes.c_void_p(dd.ctypes.data)
station_strvar=ctypes.c_void_p(station.ctypes.data)
num_met=6
num_lev=1
synop='FM-12 SYNOP                             '
string4='                                        '

writelittler.write_obs(p,z,t,td,spd,wdir,slp,ter,xlat, xlon,date_char,num_met,num_lev,kx,dd_strvar, station_strvar,synop,string4, bogus, iseq_num, iunit)

1 个答案:

答案 0 :(得分:1)

您没有在Python中指定Fortran子例程的参数类型。此外,您不会声明/初始化传递给Fortran的变量。因此,Python不知道如何处理它。这就是它在错误消息中告诉你的内容。

由于Fortran默认使用按引用调用,argtypes为:

writelittler.write_obs.argtypes = [ POINTER(c_float), # p
                                    POINTER(c_float), # z
                                    POINTER(c_float), # t
                                    POINTER(c_float), # td
                                    POINTER(c_float), # spd
                                    POINTER(c_float), # wdir
                                    POINTER(c_int),   # kx
                                    POINTER(c_float), # slp
                                    POINTER(c_float), # ter
                                    POINTER(c_float), # xlat
                                    POINTER(c_char),  # date_char
                                    POINTER(c_char),  # dd
                                    POINTER(c_char),  # station
                                    POINTER(c_int),   # num_met
                                    POINTER(c_int),   # num_lev
                                    POINTER(c_char),  # synop
                                    POINTER(c_int),   # string4
                                    POINTER(c_bool),  # bogus
                                    POINTER(c_bool),  # string4
                                    POINTER(c_int),   # iseq_num
                                    POINTER(c_int)    # iunit
                                  ]

[这可能会简化......]

此外,Fortran子例程没有返回值:

writelittler.write_obs.restype = None

此外,您需要调整函数调用本身(在Python中):

writelittler.write_obs( p.ctypes.data_as(POINTER(c_float)), # Numpy data type
                        ctypes.byref(z), # ctypes data type
                        ctypes.byref(t),
                        ctypes.byref(td),
                        ctypes.byref(spd),
                        ctypes.byref(wdir),
                        ctypes.byref(slp),
                        ctypes.byref(ter),
                        ctypes.byref(xlat), 
                        ctypes.byref(xlon),
                        ctypes.byref(date_char),
                        ctypes.byref(num_met),
                        ctypes.byref(num_lev),
                        ctypes.byref(kx),
                        ctypes.byref(dd_strvar), 
                        ctypes.byref(station_strvar),
                        ctypes.byref(synop),
                        ctypes.byref(string4), 
                        ctypes.byref(bogus), 
                        ctypes.byref(iseq_num), 
                        ctypes.byref(iunit) )

[这未经过验证......]

现在Python知道会发生什么,你仍然需要声明缺少的变量。由于您的代码已知,因此会导致错误:

Traceback (most recent call last):
  File "test.py", line 51, in <module>
    writelittler.write_obs(p,z,t,td,spd,wdir,slp,ter,xlat, 
xlon,date_char,num_met,num_lev,kx,dd_strvar,
station_strvar,synop,string4, bogus, iseq_num, iunit)
NameError: name 'z' is not defined