2014-05-13 22 views
5

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?

+0

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

+0

@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

risposta

3

Dopo il commento di @eryksun, ho apportato le seguenti modifiche per farlo funzionare.

Modificato il argtypes a:

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')] 

E invece di passare la stringa come byref, ho cambiato in quanto segue.

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) 

È stato sufficiente passare direttamente i valori.

+1

Secondo i documenti di Intel per [Mixed Language Programming] (https://software.intel.com/sites/products/documentation/hpc/composerxe/en-us/2011Update/fortran/lin/bldaps_for/common/bldaps_bk_hndl_dtypes.htm) , le lunghezze sono aggiunte alla fine dell'elenco degli argomenti, non interlacciate. Anche l'argomento è un 'c_int32' in un processo a 32 bit e' c_int64' in un processo a 64 bit.'c_long' è sempre un' c_int32' in Windows, ma in pratica non avrete problemi se non avete bisogno di una stringa che sia più lunga di '2 ** 31 - 1'. – eryksun