ここでは、Python から Fortran 手続(サブルーチン・関数)を呼び出すプログラム例をいくつか紹介します。
この例では、
- Fortran 側で C との相互利用可能性 を用いる。
- Python 側で ctypes モジュールを用いる。
これにより、C のインターフェースを介して、Python から Fortran 手続を呼び出しています。
ここで提供されるプログラム例は、Fortran Builder のテンプレート「C から Fortran を呼び出す例」の C メインプログラムを Python に書き換えたものです。従って、基本的には Fortran Builder のテンプレート「C から Fortran を呼び出す例」と同じ実行結果が得られます。
Fortran 手続の DLL 化については「DLL を Python から利用する例」をご参照ください。
- Example 0: Hello World
- Example 1: float 型(実数型)配列を Fortran に渡す
- Example 2: float 型(実数型)配列のポインタを Fortran に渡す
- Example 3: 文字列と整数型配列を Fortran に渡す
- Example 4: 文字型ポインタの配列を Fortran に渡す
Example 0: Hello World
Python ソースコード例:
# --- Example 0: Hello World --------------------------------------------------- # # Fortran interface: # # subroutine hello() bind (c) # end subroutine # # C prototype: # # void hello(void); # # ------------------------------------------------------------------------------ from ctypes import * myflib = WinDLL("myflib.dll") myflib.hello.restype = None myflib.hello.argtypes = [] myflib.hello()
(参考) 上記の Python ソースコード例と同等な C ソースコード例を示します。
[ main.c ]
extern void hello(void); int main(int argc, char *argv[]) { hello(); return 0; }
呼び出し対象の Fortran プログラムです。これを DLL 化してください。
[ sub.f90 ]
subroutine hello() bind(c) implicit none print *, 'Hello World' end subroutine
実行結果:
Hello World
Example 1: float 型(実数型)配列を Fortran に渡す
Python ソースコード例:
# --- Example 1: Passing a float (Real) array to Fortran ----------------------- # # Fortran interface: # # function dot_product_r(x, y, n) bind(c) # real(c_float), intent(in) :: x(n), y(n) # integer(c_int), value, intent(in) :: n # real(c_float) dot_product_r # end function # # C prototype: # # float dot_product_r(float x[], float y[], int n); # # ------------------------------------------------------------------------------ import numpy as np from ctypes import * myflib = WinDLL("myflib.dll") myflib.dot_product_r.restype = c_float myflib.dot_product_r.argtypes = [ np.ctypeslib.ndpointer(dtype = np.float32), np.ctypeslib.ndpointer(dtype = np.float32), c_int] N_ELTS = 10 x = np.empty(N_ELTS, dtype = np.float32) y = np.empty(N_ELTS, dtype = np.float32) for i in range(0, N_ELTS): x[i] = i * 0.5 y[i] = N_ELTS - i * 0.5 print("Dot product of [", end = "") for i in range(0, N_ELTS): print(" {0:g}".format(x[i]), end = "") print(" ]") print("and [", end = "") for i in range(0, N_ELTS): print(" {0:g}".format(y[i]), end = "") print("]") r = myflib.dot_product_r(x, y, N_ELTS) print("is {0:g}".format(r))
(参考) 上記の Python ソースコード例と同等な C ソースコード例を示します。
[ cmain.c ]
#include <stdio.h> /* * External Fortran function that computes the dot product * of two C float vectors. */ extern float dot_product_r(float x[],float y[],int n); #define N_ELTS 10 int main(int argc,char *argv[]) { float x[N_ELTS],y[N_ELTS]; int i; /* * Give X and Y some values. */ for (i=0; i<N_ELTS; i++) { x[i] = i*0.5f; y[i] = N_ELTS - i*0.5f; } /* * Display the value of X and Y, and... */ printf("Dot product of ["); for (i=0; i<N_ELTS; i++) printf(" %g",x[i]); printf(" ]\nand ["); for (i=0; i<N_ELTS; i++) printf(" %g",y[i]); /* * Display the dot product. */ printf("]\nis %g\n",dot_product_r(x,y,N_ELTS)); return 0; }
呼び出し対象の Fortran プログラムです。これを DLL 化してください。
[ dpr.f90 ]
! ! Provide Fortran DOT_PRODUCT for C float. ! ! Prototype: ! float dot_product_r(float x[],float y[],int n); ! Function dot_product_r(x,y,n) Bind(C) Use Iso_C_Binding Implicit None Integer(C_int),Value,Intent(In) :: n Real(C_float),Intent(In) :: x(n),y(n) Real(C_float) dot_product_r Intrinsic Dot_Product dot_product_r = Dot_Product(x,y) End Function
実行結果:
Dot product of [ 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 ] and [ 10 9.5 9 8.5 8 7.5 7 6.5 6 5.5] is 153.75
Example 2: float 型(実数型)配列のポインタを Fortran に渡す
Python ソースコード例:
# --- Example 2: Passing float (Real) array pointers to/from Fortran ----------- # # Fortran interface: # # type, bind(c) :: matrix # type(c_ptr) :: addr # integer(c_size_t) :: m, n # end type # # function c_matmul_r(a, b) result(c) bind(c, name = 'Matrix_Multiply_r') # type(matrix), intent(in), value :: a, b # type(matrix) :: c # end function # # subroutine c_dealloc_mat(c) bind(c, name = 'Fortran_Deallocate_Matrix') # type(matrix), value :: c # end subroutine # # C prototype: # # typedef struct { # float *addr; # size_t m, n; # } Matrix; # # Matrix Matrix_Multiply_r(Matrix a, Matrix b); # # void Fortran_Deallocate_Matrix(Matrix c); # # ------------------------------------------------------------------------------ import numpy as np from ctypes import * myflib = WinDLL("myflib.dll") class Matrix(Structure): _fields_ = [ ("addr", POINTER(c_float)), ("m", c_size_t), ("n", c_size_t)] myflib.Matrix_Multiply_r.restype = Matrix myflib.Matrix_Multiply_r.argtypes = [Matrix, Matrix] myflib.Fortran_Deallocate_Matrix.restype = None myflib.Fortran_Deallocate_Matrix.argtypes = [Matrix] def show_matrix(name, m): print("{0} (size {1} by {2}) =".format(name, str(m.m), str(m.n))) for i in range(0, m.m): print("( ", end = "") for j in range(0, m.n): print("{0:12.2f}".format(m.addr[i * m.n + j]), end = "") print(" )") A_M = 3 A_N = 4 a_addr = np.empty([A_M * A_N], dtype = np.float32) k = 0 for i in range(0, A_M): for j in range(0, A_N): a_addr[i * A_N + j] = k k += 1 a = Matrix(a_addr.ctypes.data_as(POINTER(c_float)), A_M, A_N) show_matrix("A", a) B_M = 4 B_N = 5 b_addr = np.empty([B_M * B_N], dtype = np.float32) k = 0 for i in range(0, B_M): for j in range(0, B_N): b_addr[i * B_N + j] = k k += 1 b = Matrix(b_addr.ctypes.data_as(POINTER(c_float)), B_M, B_N) show_matrix("B", b) c = myflib.Matrix_Multiply_r(a, b) show_matrix("Product(C)", c) myflib.Fortran_Deallocate_Matrix(c)
(参考) 上記の Python ソースコード例と同等な C ソースコード例を示します。
[ cmain.c ]
#include <stdio.h> #include <stddef.h> #include <stdlib.h> /* * Data type for a Real (float) matrix. */ typedef struct { float *addr; size_t m,n; } Matrix; /* * C function to display a matrix. */ void show_matrix(const char *name,Matrix m); /* * External Fortran function that does Matrix Multiply. */ extern Matrix Matrix_Multiply_r(Matrix a,Matrix b); /* * External Fortran function to deallocate an matrix array pointer. */ extern void Fortran_Deallocate_Matrix(Matrix c); /* * We will multiply a 3x4 matrix by a 4x5 matrix, * producing a 3x5 result. */ #define A_M 3 #define A_N 4 #define B_M 4 #define B_N 5 int main(int argc,char *argv[]) { Matrix a,b,c; int i,j,k; /* * Allocate and describe the input matrices. */ a.m = A_M; a.n = A_N; a.addr = (float *)malloc(a.m*a.n*sizeof(float)); b.m = B_M; b.n = B_N; b.addr = (float *)malloc(b.m*b.n*sizeof(float)); /* * Give A and B some values. */ k = 0; for (i=0; i<A_M; i++) for (j=0; j<A_N; j++) { a.addr[i*A_N+j] = k++; } k = 0; for (i=0; i<B_M; i++) for (j=0; j<B_N; j++) { b.addr[i*B_N+j] = k++; } /* * Display input matrices. */ show_matrix("A",a); show_matrix("B",b); /* * Calculate the result. */ c = Matrix_Multiply_r(a,b); /* * Show the result. */ show_matrix("Product(C)",c); /* * Deallocate the input matrices; these were allocated in C, * therefore must be deallocated in C. */ free(a.addr); free(b.addr); /* * Deallocate the result; this was allocated in Fortran, * therefore must be deallocated in Fortran. */ Fortran_Deallocate_Matrix(c); /* * End of program. */ return 0; } /* * The matrix-displaying function. */ void show_matrix(const char *name,Matrix m) { int i,j; printf("%s (size %d by %d) =\n",name,(int)m.m,(int)m.n); for (i=0; i<m.m; i++) { fputs("( ",stdout); for (j=0; j<m.n; j++) printf("%12.2f",m.addr[i*m.n+j]); fputs(" )\n",stdout); } }
呼び出し対象の Fortran プログラムです。これを DLL 化してください。
[ fmat.f90 ]
Module matrix_multiply_for_c Use Iso_C_Binding Implicit None Type,Bind(C) :: matrix Type(C_ptr) :: addr Integer(C_size_t) :: m,n End Type Contains Function c_matmul_r(a,b) Result(c) Bind(C,Name='Matrix_Multiply_r') Type(matrix),Intent(In),Value :: a,b Type(matrix) :: c Real(C_float),Pointer :: fa(:,:),fb(:,:),fc(:,:) ! ! Get the input array pointers (transposed because C). ! Call C_F_Pointer(a%addr,fa,[a%n,a%m]) Call C_F_Pointer(b%addr,fb,[b%n,b%m]) ! ! Allocate the result. ! Allocate(fc(b%n,a%m)) ! ! Compute the result value. ! ! C arrays are stored in "row-major" format, ! this is the transpose of the Fortran (column-major) format; ! so we need to transpose both the input arrays, and the result. ! fc = Transpose(Matmul(Transpose(fa),Transpose(fb))) ! ! Store the result info. ! c%m = a%m c%n = b%n c%addr = C_loc(fc(1,1)) End Function Subroutine c_dealloc_mat(c) Bind(C,Name='Fortran_Deallocate_Matrix') Type(matrix),Value :: c Real(C_float),Pointer :: fc(:,:) Call C_F_Pointer(c%addr,fc,[c%n,c%m]) Deallocate(fc) End Subroutine End Module
実行結果:
A (size 3 by 4) = ( 0.00 1.00 2.00 3.00 ) ( 4.00 5.00 6.00 7.00 ) ( 8.00 9.00 10.00 11.00 ) B (size 4 by 5) = ( 0.00 1.00 2.00 3.00 4.00 ) ( 5.00 6.00 7.00 8.00 9.00 ) ( 10.00 11.00 12.00 13.00 14.00 ) ( 15.00 16.00 17.00 18.00 19.00 ) Product(C) (size 3 by 5) = ( 70.00 76.00 82.00 88.00 94.00 ) ( 190.00 212.00 234.00 256.00 278.00 ) ( 310.00 348.00 386.00 424.00 462.00 )
Example 3: 文字列と整数型配列を Fortran に渡す
Python ソースコード例:
# --- Example 3: Passing a Character string and Integer array to Fortran ------- # # Fortran interface: # # function open_unformatted(cname) bind(c) result(unit) # character(1, c_char), intent(in) :: cname(*) # integer(c_int) :: unit # end function # # subroutine write_unformatted_integer_array(unit, array, n) bind(c) # integer(c_int), value :: unit, n # integer(c_int), intent(in) :: array(n) # end subroutine # # subroutine read_unformatted_integer_array(unit, array, n) bind(c) # integer(c_int), value :: unit, n # integer(c_int), intent(out) :: array(n) # end subroutine # # subroutine close_unformatted(unit) bind(c) # integer(c_int), value :: unit # end subroutine # # C prototype: # # int open_unformatted(char *name); # # void write_unformatted_integer_array(int, int *, int); # # void read_unformatted_integer_array(int, int *, int); # # void close_unformatted(int); # # ------------------------------------------------------------------------------ import sys import numpy as np from ctypes import * myflib = WinDLL("myflib.dll") myflib.open_unformatted.restype = c_int myflib.open_unformatted.argtypes = [c_char_p] myflib.write_unformatted_integer_array.restype = None myflib.write_unformatted_integer_array.argtypes = [ c_int, np.ctypeslib.ndpointer(dtype = np.intc), c_int] myflib.read_unformatted_integer_array.restype = None myflib.read_unformatted_integer_array.argtypes = [ c_int, np.ctypeslib.ndpointer(dtype = np.intc), c_int] myflib.close_unformatted.restype = None myflib.close_unformatted.argtypes = [c_int] D_NUM = 200 x = np.empty(D_NUM, dtype = np.intc) y = np.empty(D_NUM, dtype = np.intc) for i in range(0, D_NUM): x[i] = i * 10 unit = myflib. open_unformatted(b"example.dat") myflib.write_unformatted_integer_array(unit, x, D_NUM) myflib.close_unformatted(unit) unit = myflib.open_unformatted(b"example.dat") myflib.read_unformatted_integer_array(unit, y, D_NUM) myflib.close_unformatted(unit) for i in range(0, D_NUM): if y[i] != x[i]: print("Read check FAILED {0} != {1}".format(y[i], x[i])) sys.exit(2) print("Write of example.dat finished, read check ok.")
(参考) 上記の Python ソースコード例と同等な C ソースコード例を示します。
[ cmain.c ]
#include <stdio.h> /* * Fortran procedures to do Fortran unformatted file input/output, * with integer arrays. */ extern int open_unformatted(char *name); extern void write_unformatted_integer_array(int,int *,int); extern void read_unformatted_integer_array(int,int *,int); extern void close_unformatted(int); int main(int argc,char *argv[]) { int i,x[200],y[100],unit; /* Initialise Fortran Runtime System. */ f90_init(argc,argv); /* * Store some values in an integer array. */ for (i=0; i<200; i++) x[i] = i*10; /* * Write the integer array to an unformatted file. */ unit = open_unformatted("example.dat"); write_unformatted_integer_array(unit,x,sizeof(x)/sizeof(int)); close_unformatted(unit); /* * Read part of the file back to check that it was written correctly. */ unit = open_unformatted("example.dat"); read_unformatted_integer_array(unit,y,sizeof(y)/sizeof(int)); close_unformatted(unit); for (i=0; i<100; i++) if (y[i]!=x[i]) { printf("Read check FAILED %d != %d\n",y[i],x[i]); return 2; } /* * Finished. */ printf("Write of example.dat finished, read check ok.\n"); return 0; }
呼び出し対象の Fortran プログラムです。これらを DLL 化してください。
(2つのソースファイル util.f90 と unf.f90 で構成されています。)
[ util.f90 ]
Module util Implicit None Contains ! ! Return a copy of a C string. ! The result is ALLOCATABLE so the space will be automatically ! recovered after the call. ! Function fstring(string) Use Iso_C_Binding Character(1,C_char),Intent(In) :: string(*) Character(:,C_char),Allocatable :: fstring Integer i,len len = 1 Do While (string(len)/=C_null_char) len = len + 1 End Do len = len - 1 Allocate(Character(len,C_char) :: fstring) Do i=1,len fstring(i:i) = string(i) End Do End Function End Module
[ unf.f90 ]
Module unformatted_file_functions_for_C Use Iso_C_Binding Implicit None Contains Function open_unformatted(cname) Bind(C) Result(unit) Use util Character(1,C_char),Intent(In) :: cname(*) Integer(C_int) :: unit Open(File=fstring(cname),Newunit=unit,Form='Unformatted', & Access='Sequential') End Function Subroutine close_unformatted(unit) Bind(C) Integer(C_int),Value :: unit Close(unit) End Subroutine Subroutine write_unformatted_integer_array(unit,array,n) Bind(C) Integer(C_int),Value :: unit,n Integer(C_int),Intent(In) :: array(n) Write(unit) array End Subroutine Subroutine read_unformatted_integer_array(unit,array,n) Bind(C) Integer(C_int),Value :: unit,n Integer(C_int),Intent(Out) :: array(n) Read(unit) array End Subroutine End Module
実行結果:
Write of example.dat finished, read check ok.
Example 4: 文字型ポインタの配列を Fortran に渡す
Python ソースコード例:
# --- Example 4: Passing an array of Character pointers to Fortran ------------- # # Fortran interface: # # subroutine display_c_table(table) bind(c, name = 'display_table_info') # type(c_ptr) table(*) # end subroutine # # C prototype: # # void display_table_info(char *table[]); # # ------------------------------------------------------------------------------ from ctypes import * myflib = WinDLL("myflib.dll") myflib.display_table_info.restype = None myflib.display_table_info.argtypes = [POINTER(c_char_p)] table = [ b"Entry One", b"Entry Two", b"Entry Three", b"And this entry is the longest one", b"Entry Five", b"Another entry that is very long..", None] ArrayType = c_char_p * len(table) table = ArrayType(*table) myflib.display_table_info(table)
(参考) 上記の Python ソースコード例と同等な C ソースコード例を示します。
[ cmain.c ]
#include <stdio.h> /* * Fortran procedure to display information about the table. */ extern void display_table_info(char *table[]); int main(int argc,char *argv[]) { static char *table[] = { "Entry One", "Entry Two", "Entry Three", "And this entry is the longest one", "Entry Five", "Another entry that is very long..", (char *)0 }; display_table_info(table); return 0; }
呼び出し対象の Fortran プログラムです。これらを DLL 化してください。
(2つのソースファイル chptrarray.f90 と display.f90 で構成されています。)
[ chptrarray.f90 ]
! ! Utility module ! Module util_char_ptr Use Iso_C_Binding Implicit None ! ! Derived type for wrapping a character string pointer in Fortran. ! Type char_string_ptr Character(:,C_char),Pointer :: value => Null() End Type Contains ! ! Utility function to convert a C array of char pointers, ending with a ! null pointer, into an array of character string pointers in Fortran. ! Function ctable_to_ftable(cptr) Result(r) Type(C_ptr) :: cptr(*) Type(char_string_ptr),Pointer :: r(:) Integer i,n n = 1 Do While(C_associated(cptr(n))) n = n + 1 End Do n = n - 1 Allocate(r(n)) Do i=1,n r(i)%value => c_charptr_to_f_charptr(cptr(i)) End Do End Function ! ! Utility routine to turn a C pointer to a null-terminated string ! into a Fortran CHARACTER pointer to that string. The function ! returns a deferred-length CHARACTER pointer that is associated with ! the C string, and whose length (LEN) is the length of the string. ! Function c_charptr_to_f_charptr(ccp) Result(result) Type(C_ptr),Intent(In),Value :: ccp Character(:,C_char),Pointer :: result Interface Function strlen(p) Bind(C) Import C_ptr,C_size_t Type(C_ptr),Value :: p Integer(C_size_t) strlen End Function End Interface result => convert_cptr(ccp,strlen(ccp)) Contains ! ! This uses a variable-length CHARACTER pointer because the ! function C_F_pointer has no other way of encoding the length. ! Function convert_cptr(p,len) Type(C_ptr),Intent(In) :: p Integer(C_size_t),Intent(In) :: len Character(len,C_char),Pointer :: convert_cptr Call C_F_pointer(p,convert_cptr) End Function End Function End Module
[ display.f90 ]
! ! Subroutine to display the maximum string value in a table, ! and the maximum string length in that table. ! ! The table is simply a C array of C char strings (null-terminated). ! Subroutine display_c_table(table) Bind(C,Name='display_table_info') Use util_char_ptr Implicit None Type(C_Ptr) table(*) Type(char_string_ptr),Pointer :: ftable(:) Integer i,maxlen,maxlenpos,maxstrpos ! ! Start by constructing an array of Fortran character pointers to the table. ! ftable => ctable_to_ftable(table) If (Size(ftable)==0) Then Print *,'Empty table' Else maxlen = Len(ftable(1)%value) maxlenpos = 1 maxstrpos = 1 Do i=2,Size(ftable) If (Len(ftable(i)%value)>maxlen) Then maxlen = Len(ftable(i)%value) maxlenpos = i End If If (ftable(i)%value>ftable(maxstrpos)%value) maxstrpos = i End Do Print *,'Maximum string value is "',ftable(maxstrpos)%value,'"' Print *,'Maximum string length is',maxlen Print *,'The first string with that length is "',ftable(maxlenpos)%value,'"' End If Deallocate(ftable) End Subroutine
実行結果:
Maximum string value is "Entry Two" Maximum string length is 33 The first string with that length is "And this entry is the longest one"