ここでは、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"
