Reputation: 49
I'm trying to use a Fortran module in Python. I have several arrays in Python -- both numeric and string. I defined the arrays in Fortran and Python, but I think that I used the wrong type for the numeric arrays. I get an error that converting the first argument (a numeric array) failed. What types should I use?
Error:
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)
Upvotes: 2
Views: 722
Reputation: 18098
You did not specify the argument types of the Fortran subroutine in Python. Furthermore, you do not declare/initialize the variables you pass to Fortran either. Therefore, Python does not know what to do with it. That is what it is telling you in the error message.
As Fortran uses call by reference by default, the argtypes
are:
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
]
[This can probably be simplified...]
Additionally, Fortran subroutines do not have a return value:
writelittler.write_obs.restype = None
Furthermore, you need to adjust the function call itself (in 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) )
[This is not validated...]
Now that Python knows what to expect, you still need to declare the missing variables. As your code is know, it will result in the error:
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
Upvotes: 2