ページの先頭です

Fortranの手続きをPythonから呼び出すポータブルかつ簡便な方法

2021年3月1日 情報通信研究部 松村 洋祐

はじめに

近年、AIや機械学習、データサイエンス等の需要の高まりを受け、それらのライブラリを豊富に持つPythonが人気である。筆者の業務分野である格子生成やCAD等の分野においても、AI等との連携が話題となることが増えてきた。

しかし、格子生成やCAD等のソフトウェアには、従来から科学技術計算で多く使われてきたFortranで書かれた過去からの膨大なソースコードの資産がある。この膨大な資産を新たにPythonで書き直すためには大きな労力を要することが予想されるため、現実的には不可能であることが多い。このため、Pythonの持つ豊富なライブラリの恩恵を受けたければ、Fortranの手続き(サブルーチン、関数…)をPythonから呼び出して使用することが必要となる。本稿では、筆者が業務において実際に使っている、Fortranの手続きをPythonから呼び出すポータブルかつ簡便な方法を紹介する。

Pythonは拡張性が高く、C言語で書かれた共有ライブラリの中にある関数を呼び出すことが容易である。そのため、以下の対応を行えばFortranの手続きをPythonから呼び出せるようになる。

  • Fortranの手続きをC言語から呼び出せる形にする
  • Fortranのソースコードから共有ライブラリを作成する

これらについて、Linuxを例として記述する。添付のサンプルコードも適宜参照いただきたい。

Fortranの手続きをC言語から呼び出せる形にする

Fortranの手続きをC言語から呼び出す古典的な方法は、C言語で呼び出すFortranの手続き名の最後尾に(アンダースコア)をつける、引数を参照渡しにする、文字列変数の最後に長さをつける、などを行う方法である。

例えば、Fortranで次のように記述されたサブルーチンを

式1

C言語から次のように呼び出すことは可能であった。

式2

ただ、Fortranで書かれた手続き名がライブラリの中でどのようなシンボルになるか(いくつアンダースコアがつくか/つかないか、大文字/小文字、モジュール名の付加…)はコンパイラ依存であり、この方法は汎用性が高いとは言い難い。汎用マクロプロセッサのm4で複数のコンパイラごとのシンボルを生成するマクロを使い、configure時に自動判定する方法もあるが、複雑になりメンテナンス性が悪くなるものだった。

しかし、Fortran 2003より、C言語との相互運用性(Interoperability with C)という機能が追加され、Fortranの手続きをC言語から呼び出して使用する標準的な方法が確立された。

例えば、Fortranで次のように記述すれば、

式3

C言語から必ず次のように呼び出せる。

式4

このとき、Pythonにおいて、C言語の共有ライブラリの関数呼び出しを可能にする機能であるctypesを用いて、次のように記述することで、Fortranの手続きを呼び出すことが可能となる。

式5

なお、既存の膨大なFortranの外部手続きのインタフェイスとその呼び出しをすべて書き直すことも困難であるため、既存のFortranの次のような手続きに対して、

式6

次のようなラッパーを追加することが現実的であると考える。

式7

このラッパーをスクリプト等で自動生成することも可能である。

Fortranのソースコードから共有ライブラリを作成する

Fortranのコンパイル時にオプションをつければ、共有ライブラリを作成することはできる。ただし、共有ライブラリを作成するためのオプションは、コンパイラやプラットフォームごとに異なるものであり、個々の開発者が環境ごとに一つ一つ調べて対応するのでは、簡便とは呼べないだろう。

autotools(autoconf, automake, libtool)を利用して共有ライブラリを作成することもできるが、“autohell”とも呼ばれる複雑な構成のため、将来的なメンテナンスが困難となることも有り得る。また、autotoolsから生成されるビルド環境をWindowsで利用する場合には、Cygwin等のUNIX互換環境が必要となる。

そこで、CMakeを利用する。CMakeは、Kitware(VTK、ParaView等の開発元)により開発されたオープンソース、クロスプラットフォームのビルドツールであり、主要なLinuxディストリビューションのパッケージにも含まれている。近年では、科学技術計算分野以外においても、CMakeは広く利用されはじめている。

CMakeでは、CMakeLists.txtというファイルにビルドの設定を記述したうえで、cmakeコマンドもしくはccmakeコマンドを用いてMakefileを生成し、その後makeコマンドでコンパイルを行う。

Fortranのソースコードから共有ライブラリを作成する場合は、CMakeLists.txtに次のような記述を追加すればよい。

式8

ここで作成された共有ライブラリをPythonで呼び出すことにより、Fortranの手続きをPythonから使用できるようになる。

また、前述のラッパーの自動生成スクリプトをビルドに組み込むことも可能である。

その他の注意事項

その他、注意すべき点をいくつか列挙する。

配列のオリジンについて

Fortranの配列は1オリジン(配列先頭要素のインデックスが1)なのに対して、Pythonの配列は0オリジン(配列先頭要素のインデックスが0)である。このため、Fortranでの配列をreal(8) :: data(0:n)のように宣言し、0オリジンとしてPythonと合わせる方法もある。

しかし、手続きで渡されるのは配列の先頭ポインタであり、インデックスからのオフセットの計算は各言語がそれぞれ処理を行うものである。そのため、Fortranの手続きをPythonで新たに書き直すのでなければ、配列のオリジンの違いを気にする必要はほとんどない。

(PythonからFortranにインデックス値を渡す際に+1、Fortranからのインデックス値の戻り値に-1する必要はあるが、それがベストプラクティスであり、筆者の業務分野では、Fortranで書かれたソルバーの計算結果用の可視化ソフトウェアをC/C++言語等で書く場合、一般的に行われている方法である。)

多次元配列について

科学技術計算では、多次元配列が多用される。

Fortranの多次元配列はcolumn-major order(列優先)であり、左側の添字が最も早く変化するようにメモリ上に格納される。これに対し、Pythonでは多次元配列はリストのリストであり、Fortranとはメモリへの格納方法が異なる。そのため、PythonとFortranで多次元配列を共有するためには工夫が必要となる。

Pythonの多次元配列をFortranで共有する

PythonでFortranに渡す多次元配列を確保する必要がある場合は、NumPyの多次元データ配列構造ndarrayを利用して、Pythonで次のように多次元配列を確保し、

式9

次のようにFortranの手続きを呼び出す。

式10

Fortranでは、次のように受ければよい。

式11

(numpy.empty()でのndarrayの確保の際に、引数orderにorder='F'としてFortranスタイル(=列優先)を指定する考え方もある。しかし、後述のFortranの多次元配列をPythonに共有する際にはorder='F'の指定が不可能である。また、筆者の業務分野では、Fortranで書かれたソルバーの計算結果用の可視化ソフトウェアをC/C++言語等で書く場合、多次元配列をdata[k][j][i]としてrow-major order(行優先)で扱うことが一般的である。)

Fortranの多次元配列をPythonで共有する

Fortranで確保した多次元配列をPythonで共有するためには、Fortranで次のように多次元配列を確保し、

式12

次のように先頭アドレスを返す関数を追加する。

式13

Pythonでは、次のようにctypeslib.as_array()を用いてndarrayとして扱うことで多次元配列を共有できる。

式14

おわりに

本稿では、筆者が業務において実際に使っている、Fortranの手続きをPythonから呼び出すポータブルかつ簡便な方法を紹介した。

この方法により、大きな労力をかけることなくFortranで記述された過去からの膨大な資産がPythonで利用可能となり、Pythonの持つAIや機械学習、統計等の豊富なライブラリも使えるようになる。また、matplotlibによる簡易な可視化や設定ファイルのスクリプト化等も容易となる。

本稿が、効率的なツールの開発、さらには、科学技術分野とAIとの連携を深める一助となれば幸いである。

関連情報

この執筆者はこちらも執筆しています

ページの先頭へ