Sto provando a caricare una DLL in Python 2.7 usando i ctypes. La DLL è stata scritta utilizzando Fortran e contiene più subroutine. Sono stato in grado di impostare correttamente un paio delle funzioni esportate che i puntatori long
e double
come argomenti.Passare la stringa a Fortran DLL usando ctypes e Python
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)
Fin qui, tutto bene.
Il problema sorge quando provo un'altra subroutine chiamato Function2
che ha bisogno di alcune stringhe come input. Le stringhe sono tutte di lunghezza fissa (255) e richiedono anche la lunghezza di ciascuno dei parametri della stringa.
La funzione è implementata in Fortran come segue:
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)
Il prototipo di funzione in Python è configurato come segue
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')]
E io lo chiamo con:
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)
Le stringhe sono essenzialmente percorsi. La funzione Function2
non riconosce i percorsi e mostra un messaggio di errore con alcuni caratteri non leggibili alla fine, come ad esempio:
forrtl: severe (43): file name specification error, unit 16, D:\Users\xxxxxxx\Documents\xxxxx\ωa
.
Quello che volevo che la funzione ricevesse era D:\Users\xxxxxxx\Documents\xxxxx\
. Ovviamente, le stringhe non vengono passate correttamente.
Ho letto che Python utilizza stringhe terminate NULL
. Può essere un problema mentre si passano le stringhe a una dll di Fortran? Se è così, come faccio ad aggirarlo?
Qualche consiglio?
OK. Cambierà il 'argtypes' per passare in base al valore. Per quanto riguarda il commento precedente, hai ragione. Dovrebbe essere la funzione del prototipo. Ho apportato le modifiche sopra. Doveva semplificare un po 'di codice dato che era stato originariamente implementato come una classe. – d0m1n0
@eryksun Che ha aiutato. Ora le stringhe vengono passate correttamente. La DLL sembra avere ancora problemi con 'Function2'. Dovrà lavorare con i suoi autori per quello. – d0m1n0