使用ctypes和Python将字符串传递给Fortran DLL [英] Passing string to Fortran DLL using ctypes and Python

查看:629
本文介绍了使用ctypes和Python将字符串传递给Fortran DLL的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用ctypes加载Python 2.7中的DLL。该DLL是使用Fortran编写的,并在其中有多个子程序。我可以成功地设置几个导出的函数,即 long double 指针作为参数。

  import ctypes as C 
import numpy as np

dll = C.windll.LoadLibrary('C:\\\Temp\\program.dll')
_cp_from_t = getattr(dll,CP_FROM_T)
_cp_from_t.restype = C.c_double
_cp_from_t.argtypes = [C.POINTER(C.c_longdouble),
np.ctypeslib.ndpointer(C.c_longdouble)]

#Mixture Rgas函数
_mix_r = getattr (dll,MIX_R)
_mix_r.restype = C.c_double
_mix_r.argtypes = [np.ctypeslib.ndpointer(dtype = C.c_longdouble)]

def cp_from_t (组成,温度):
计算燃料成分和温度时的BTU / lb / R中的Cp

:param composition:包含燃料组成的numpy数组
:参数温度:燃料温度
:返回:Cp
:rtype:float

return _cp_from_t(C.byref(C.c_double(temp)),composition)

def mix_r(composition):
给定组合的常数。
:rtype:float
:param composition:包含燃料组成的数组数组

返回_mix_r(组合)

#在这一点上,我可以通过一个numpy数组作为组合,我可以得到
#计算值没有问题
comps = np.array([0,0,12.0,23.0,33.0,10,5.0] )
temp = 900.0

cp = cp_from_t(comps,temp)
rgas = mix_r(comps)

到目前为止,很好。



当我尝试另一个子程序调用 Function2 ,它们需要一些字符串作为输入,字符串都是固定长度(255),并且还要求每个字符串参数的长度



该功能在Fortran中实现如下:

 子例程FUNCTION2(localBasePath,localTempPath,InputFileName,Model,DataArray,ErrCode)
!DEC $ ATTRIBUTES STDCALL,REFERENCE,ALIAS:'FUNCTION2',DLLEXPORT :: FUNC TION2
隐式无
字符* 255 localBasePath,localTempPath,InputFileName
整数* 4模型(20),ErrCode(20)
Real * 8 DataArray(900)

Python中的函数原型设置如下

  function2 = getattr(dll,'FUNCTION2')
function2.argtypes = [C.POINTER(C.c_char_p),C.c_long,
C.POINTER(C.c_char_p),C.c_long,
C.POINTER(C.c_char_p),C.c_long,
np.ctypeslib.ndpointer(C.c_long,flags ='F_CONTIGUOUS '),
np.ctypeslib.ndpointer(C.c_double,flags ='F_CONTIGUOUS'),
np.ctypeslib.ndpointer(C.c_long,flags ='F_CONTIGUOUS')]

我使用以下命令:

  base_path =D:\\Users\\xxxxxxx\\\Documents\\\ xxxxx\\.ljust(255)
temp_path =D:\\Users\\xxxxxxx\\\Documents\\xxxxx\\temp.ljust(255 )
inp_file =inp.txt.ljust(255)

function2(C.byref(C.c_char_p(base_path)),
C.c_long(len(base_path )),
C.byref(C.c_char_p(temp_dir)),
C.c_long(len(temp_dir))),
C.byref(C.c_char_p(inp_file)),
C.c_long(len(inp_file)),
model_array,
data_array,
error_array)

字符串本质上是路径。函数 Function2 不能识别路径,并在最后证明有一些不可读字符的错误消息,例如:



forrtl:severe(43):文件名称规范错误,单元16,D:\Users\xxxxxxx\Documents\xxxxx\œâa



我想要接收的功能是 D:\Users\xxxxxxx\Documents\xxxxx\ 。显然,这些字符串没有正确传递。



我已经阅读了Python使用 NULL 终止的字符串。在将字符串传递给Fortran dll时,这可能是一个问题吗?如果是这样,我该如何解决?



任何建议?

解决方案

更改了 argtypes to:

  function2 = getattr(dll,'FUNCTION2')
function2.argtypes = [C.c_char_p,C .c_long,
C.c_char_p,C.c_long,
C.c_char_p,C.c_long,
np.ctypeslib.ndpointer(C.c_long,flags ='F_CONTIGUOUS'),
np.ctypeslib.ndpointer(C.c_double,flags ='F_CONTIGUOUS'),
np.ctypeslib.ndpointer(C.c_long,flags ='F_CONTIGUOUS')]

而不是将字符串传递为 byref ,我将其更改为以下内容。 p>

  base_path =D:\\Users\\xxxxxxx\\Documents\\xxxxx\\\ \\.ljust(255)
temp_path =D: \\Users\\xxxxxxx\\\Documents\\xxxxx\\temp.ljust(255)
inp_file =inp.txt.ljust(255)

function2(base_path,len(base_path),temp_dir,len(temp_dir),inp_file,len(inp_file),
model_array,data_array,error_array)
/ pre>

直接传递值足够了。


I am trying to load a DLL in Python 2.7 using ctypes. The DLL was written using Fortran and has multiple subroutines in it. I was able to successfully set up couple of the exported functions that that long and double pointers as arguments.

import ctypes as C
import numpy as np

dll = C.windll.LoadLibrary('C:\\Temp\\program.dll')
_cp_from_t = getattr(dll, "CP_FROM_T")
_cp_from_t.restype = C.c_double
_cp_from_t.argtypes = [C.POINTER(C.c_longdouble),
                    np.ctypeslib.ndpointer(C.c_longdouble)]

# Mixture Rgas function
_mix_r = getattr(dll, "MIX_R")
_mix_r.restype = C.c_double
_mix_r.argtypes = [np.ctypeslib.ndpointer(dtype=C.c_longdouble)]

def cp_from_t(composition, temp):
    """ Calculates Cp in BTU/lb/R given a fuel composition and temperature.

    :param composition: numpy array containing fuel composition
    :param temp: temperature of fuel
    :return: Cp
    :rtype : float
    """
    return _cp_from_t(C.byref(C.c_double(temp)), composition)

def mix_r(composition):
    """Return the gas constant for a given composition.
    :rtype : float
    :param composition: numpy array containing fuel composition
    """
    return _mix_r(composition)

# At this point, I can just pass a numpy array as the composition and I can get the 
# calculated values without a problem
comps = np.array([0, 0, 12.0, 23.0, 33.0, 10, 5.0])
temp = 900.0

cp = cp_from_t(comps, temp)
rgas = mix_r(comps)

So far, so good.

The problem arises when I try another subroutine called Function2 which needs some strings as input. The strings are all fixed length (255) and they also ask for the length of each of the string parameters.

The function is implemented in Fortran as follows:

Subroutine FUNCTION2(localBasePath,localTempPath,InputFileName,Model,DataArray,ErrCode)
!DEC$ ATTRIBUTES STDCALL,REFERENCE, ALIAS:'FUNCTION2',DLLEXPORT :: FUNCTION2
Implicit None
Character *255 localBasePath,localTempPath,InputFileName
Integer   *4  Model(20), ErrCode(20)
Real      *8  DataArray(900)

The function prototype in Python is set up as follows

function2 = getattr(dll, 'FUNCTION2')
function2.argtypes = [C.POINTER(C.c_char_p), C.c_long,
                      C.POINTER(C.c_char_p), C.c_long,
                      C.POINTER(C.c_char_p), C.c_long,
                      np.ctypeslib.ndpointer(C.c_long , flags='F_CONTIGUOUS'),
                      np.ctypeslib.ndpointer(C.c_double, flags='F_CONTIGUOUS'),
                      np.ctypeslib.ndpointer(C.c_long, flags='F_CONTIGUOUS')]

And I call it using:

base_path = "D:\\Users\\xxxxxxx\\Documents\\xxxxx\\".ljust(255)
temp_path = "D:\\Users\\xxxxxxx\\Documents\\xxxxx\\temp".ljust(255)
inp_file = "inp.txt".ljust(255)

function2(C.byref(C.c_char_p(base_path)),
                  C.c_long(len(base_path)),
                  C.byref(C.c_char_p(temp_dir)),
                  C.c_long(len(temp_dir))),
                  C.byref(C.c_char_p(inp_file)),
                  C.c_long(len(inp_file)),
                  model_array,
                  data_array,
                  error_array)

The strings are essentially paths. The function Function2 does not recognize the paths and proves an error message with some non-readable characters at the end, such as:

forrtl: severe (43): file name specification error, unit 16, D:\Users\xxxxxxx\Documents\xxxxx\ωa.

What I wanted the function to receive was D:\Users\xxxxxxx\Documents\xxxxx\. Obviously, the strings are not passed correctly.

I have read that Python uses NULL terminated strings. Can that be a problem while passing strings to a Fortran dll? If so, how do I get around it?

Any recommendations?

解决方案

Following comment from @eryksun, I made the following changes to make it work.

Changed the argtypes to:

function2 = getattr(dll, 'FUNCTION2')
function2.argtypes = [C.c_char_p, C.c_long,
                  C.c_char_p, C.c_long,
                  C.c_char_p, C.c_long,
                  np.ctypeslib.ndpointer(C.c_long , flags='F_CONTIGUOUS'),
                  np.ctypeslib.ndpointer(C.c_double, flags='F_CONTIGUOUS'),
                  np.ctypeslib.ndpointer(C.c_long, flags='F_CONTIGUOUS')]

And instead of passing the string as byref, I changed it to the following.

base_path = "D:\\Users\\xxxxxxx\\Documents\\xxxxx\\".ljust(255)
temp_path = "D:\\Users\\xxxxxxx\\Documents\\xxxxx\\temp".ljust(255)
inp_file = "inp.txt".ljust(255)

function2(base_path, len(base_path), temp_dir, len(temp_dir), inp_file, len(inp_file), 
          model_array, data_array, error_array)

It was sufficient to pass the values directly.

这篇关于使用ctypes和Python将字符串传递给Fortran DLL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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