Fortran Builder: Python から Fortran を呼び出す例

ここでは、Python から Fortran 手続(サブルーチン・関数)を呼び出すプログラム例をいくつか紹介します。

この例では、

これにより、C のインターフェースを介して、Python から Fortran 手続を呼び出しています。

ここで提供されるプログラム例は、Fortran Builder のテンプレート「C から Fortran を呼び出す例」の C メインプログラムを Python に書き換えたものです。従って、基本的には Fortran Builder のテンプレート「C から Fortran を呼び出す例」と同じ実行結果が得られます。

Fortran 手続の DLL 化については「DLL を Python から利用する例」をご参照ください。

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"
関連情報
MENU
Privacy Policy  /  Trademarks