fortran66のブログ

fortran について書きます。

【メモ帳】Fortran から Python を呼ぶ cffi 篇

cffi にて pythonFortran から 呼び出す

以下の記事を参考に、WSL で Fotrran から Python を呼び出してみます。 記事中の呼び出し方法3、CFFI を用いる方法です。比較的簡潔なのが特徴です。

FortranPython 間での配列のやり取りも簡単なようです。

www.noahbrenowitz.com

Python 側のドキュメント

cffi.readthedocs.io

参考記事:

stackoverflow.com

Write the name of the source file first:

実行例

記事中のままでは動かなかったのですが、dynamic lib の拡張子を OS に合わせて変える他に、WSL(Linux) の場合 gfortran でのコンパイル時に、ソースファイルを先頭に持ってくる必要があるようです。Dynamic Library の path の指定も必要でした。

下の例で $ export LD_LIBRARY_PATH が抜けていました。

(cffi) dyna@dyna:~/py$ python builder.py
generating ./my_plugin.c
(already up-to-date)
the current directory is '/home/dyna/py'
(cffi) dyna@dyna:~/py$ gfortran test.f90  -L. -lplugin -o test
(cffi) dyna@dyna:~/py$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/dyna/py
(cffi) dyna@dyna:~/py$ ./test
libGL error: MESA-LOADER: failed to open swrast: /usr/lib/dri/swrast_dri.so: cannot open shared object file: No such file or directory (search paths /usr/lib/x86_64-linux-gnu/dri:\$${ORIGIN}/dri:/usr/lib/dri, suffix _dri)
libGL error: failed to load driver: swrast
Plot!
(cffi) dyna@dyna:~/py$

Python called from Fortran

なんかエラーが出てますが、デバイス・ドライバがらみで Python ではよくあることなので気にしませんw

import cffi
ffibuilder = cffi.FFI()

header = """
extern void pyplot(void);
"""

module = """
from my_plugin import ffi
import numpy as np
import matplotlib.pyplot as plt

@ffi.def_extern()
def pyplot():
    xp = np.arange(-np.pi, np.pi, 0.1)
    yp = np.sin(xp)

    plt.plot(xp, yp)
    plt.show()
    print("Plot!")
"""

with open("plugin.h", "w") as f:
    f.write(header)

ffibuilder.embedding_api(header)
ffibuilder.set_source("my_plugin", r'''
    #include "plugin.h"
''')

ffibuilder.embedding_init_code(module)
ffibuilder.compile(target="libplugin.so", verbose=True)
#ffibuilder.compile(target="plugin-1.5.*", verbose=True)
! test.f90
program call_python
  use, intrinsic :: iso_c_binding
  implicit none
  interface
     subroutine pytest() bind (c, name = 'pyplot')
     end subroutine pytest
  end interface

  call pytest()

end program call_python