Calling fortran from the outside world. Using iso_c_bindings this is a bit easier. Here you can find some fortran examples and see how you can call these functions from python using ctypes and numpy.
The fortran code.
Code Block |
---|
module test use iso_c_binding implicit none integer, parameter :: MAXSTRLEN = 512 contains ! Utility functions ! fortran character(len=*) are not compatible with c ! To be compatible with c, strings sould be copied to a c_char array function char_array_to_string(char_array, length) integer(c_int) :: length character(c_char) :: char_array(length) character(len=length) :: char_array_to_string integer :: i do i = 1, length char_array_to_string(i:i) = char_array(i) enddo end function char_array_to_string ! C ends strings with a \0 character. Add this so it is received correctly in c compatible languages function string_to_char_array(s, length) integer(c_int) :: length character :: s(*) character(c_char) :: string_to_char_array(length) integer :: i do i = 1, length string_to_char_array(i:i) = s(i) enddo string_to_char_array(i+1:i+1) = C_NULL_CHAR end function string_to_char_array ! 1 int integer(c_int) function oneint(arg1) bind(C, name="oneint") integer(c_int), intent(inout) :: arg1 arg1 = 111 oneint = 123 end function oneint ! 1 double integer(c_int) function onedouble(arg1) bind(C, name="onedouble") real(c_double), intent(inout) :: arg1 arg1 = 1.11d0 onedouble = 123 end function onedouble ! 10by10 double integer(c_int) function twobytwodouble(x) bind(C, name="twobytwodouble") real(c_double),intent(inout) :: x(2,2) x = 4 x(2,1) = 21 x(1,2) = 12 twobytwodouble = 123 end function twobytwodouble ! 10by10 double integer(c_int) function twobythreedouble(x) bind(C, name="twobythreedouble") real(c_double),intent(inout) :: x(2,3) x = 6 x(2,1) = 21 x(1,3) = 13 twobythreedouble = 123 end function twobythreedouble integer(c_int) function twobytwodoublepointer(ptr) bind(C, name="twobytwodoublepointer") type(c_ptr), intent(inout) :: ptr real(c_double), target, save :: x(2,2) x = 4 x(2,1) = 21 x(1,2) = 12 ptr=c_loc(x) twobytwodoublepointer = 123 end function twobytwodoublepointer ! 10by10 double pointer integer(c_int) function twobythreedoublepointer(ptr) bind(C, name="twobythreedoublepointer") type(c_ptr), intent(inout) :: ptr ! Save is required here for the memory to remain available after the function call real(c_double), target, save :: x(2,3) x = 6 x(2,1) = 21 x(1,3) = 13 ptr = c_loc(x) twobythreedoublepointer = 123 end function twobythreedoublepointer ! character integer(c_int) function letter(arg1) bind(C, name="letter") character(kind=c_char), intent(inout) :: arg1 arg1 = 'W' letter = 123 end function letter ! string in (string in length is not fixed but internally you need to set a fixed string length) integer(c_int) function stringin(arg1) bind(C, name="stringin") character(kind=c_char), intent(in) :: arg1(*) character(len=MAXSTRLEN) :: string string = char_array_to_string(arg1, MAXSTRLEN) write(*,*)string stringin = 123 end function stringin ! string out (requires fixed number of letters) integer(c_int) function stringout(arg1) bind(C, name="stringout") ! Output string has to be fixed character(kind=c_char), intent(out) :: arg1(MAXSTRLEN) character(len=MAXSTRLEN) :: string string = "Hello from fortran" arg1 = string_to_char_array(string, len(trim(string))) stringout = 123 end function stringout end module test |
The corresponding python code.
Code Block | ||||
---|---|---|---|---|
| ||||
#!/usr/bin/env python import numpy as np from ctypes import (CDLL, POINTER, ARRAY, c_void_p, c_int, byref,c_double, c_char, c_char_p, create_string_buffer) from numpy.ctypeslib import ndpointer import os dllpath = os.path.abspath("test.dylib") # or .dll or .so libtest = CDLL(dllpath) # Define some extra types # pointer to a double c_double_p = POINTER(c_double) # pointer to a integer c_int_p = POINTER(c_int) shape2x2=(2,2) # Pointer to a 2x2 double in fortran layout c_double2x2_c = ndpointer(shape=shape2x2, dtype="double", flags="C") c_double2x2_f = ndpointer(shape=shape2x2, dtype="double", flags="FORTRAN") # Pointer to a pointer to a 10x10 double in fortran layout c_double2x2_f_p = POINTER(c_double2x2_f) c_double2x2_c_p = POINTER(c_double2x2_c) shape3x2=(3,2) shape2x3=(2,3) # Pointer to a 2x3,3x2 double in fortran layout c_double2x3_c = ndpointer(shape=shape2x3, dtype="double", flags="C") c_double2x3_f = ndpointer(shape=shape2x3, dtype="double", flags="FORTRAN") c_double3x2_c = ndpointer(shape=shape3x2, dtype="double", flags="C") c_double3x2_f = ndpointer(shape=shape3x2, dtype="double", flags="FORTRAN") # Pointer to a pointer to a 2x3,3x2 double in fortran layout c_double2x3_f_p = POINTER(c_double2x3_f) c_double2x3_c_p = POINTER(c_double2x3_c) c_double3x2_f_p = POINTER(c_double3x2_f) c_double3x2_c_p = POINTER(c_double3x2_c) # Pointer to a character pointer c_char_p_p = POINTER(c_char_p) MAXSTRLEN=512 # Character array (Fortran can only return c_char arrays in c compatible mode) c_char_array = ARRAY(c_char,MAXSTRLEN) # Pointer to a character array c_char_array_p = POINTER(c_char_array) # oneint f = libtest.oneint f.argtypes=[c_int_p] arg1 = c_int(1) rc=f(byref(arg1)) print arg1.value # onedouble f = libtest.onedouble f.argtypes=[c_double_p] arg1 = c_double(1) rc=f(byref(arg1)) print arg1.value # 2x2 f = libtest.twobytwodouble f.argtypes=[c_double2x2_f] arg1 = np.zeros(shape2x2, order="F") rc=f(arg1) arr = np.array(arg1) print arr print arr.flags # 2x2 p f = libtest.twobytwodoublepointer f.argtypes=[c_double2x2_c_p] arg1 = c_double2x2_c() rc=f(byref(arg1)) arr = np.array(arg1) print arr print arr.flags # 2x3 f = libtest.twobythreedouble f.argtypes=[c_double2x3_f] arg1 = np.zeros(shape2x3,order="F") rc=f(arg1) arr = np.array(arg1) print arr print arr.flags # 2x3 corresponds to 3x2 p in C order, reversed from F. f = libtest.twobythreedoublepointer f.argtypes=[c_double3x2_c_p] arg1 = c_double3x2_c() rc=f(byref(arg1)) arr = np.array(arg1, order="C") print arr print arr.flags # Exchange one letter f = libtest.letter f.argtypes=[c_char_p] arg1 = c_char('H') rc=f(byref(arg1)) print arg1.value # Exchange a string (in) f = libtest.stringin f.argtypes=[c_char_array_p] arg1 = create_string_buffer('Hello from python',MAXSTRLEN) rc=f(byref(arg1)) # Exchange a string (out) f = libtest.stringout f.argtypes=[c_char_array_p] arg1 = create_string_buffer('',MAXSTRLEN) rc=f(byref(arg1)) print arg1.value del libtest |