概要
f2pyを使用するとFortranで作成したsubroutineをPythonから実行することができる。しかし、subroutineの実行時に、特に引数に配列のが含まれる場合、引数が省略されたり順番が入れ替わる場合がある。そのため、本記事では配列を受け渡す際の引数についてまとめる。
f2pyを昨日使い始めたばかりの素人なので、間違い等もあると思います。コメント等で教えていただけると助かります。
この記事は書きかけです。出張から帰ってきたらもう少しまともに書き直します。
環境
- Python 3.8.9
- f2py 1.22.4
- numpy 1.22.4
f2pyとは
Fortranで書いたサブルーチンをPython動かすためのパッケージ。
Fortranソース
以下のような(意味のない)コードを用意した。
module mod_test
implicit none
contains
subroutine test_sub(a_in, nax, nay, b_out, nbx, nby)
integer, intent(in) :: nax, nay, nbx, nby
real(8), intent(in) :: a_in(nax, nay)
real(8), intent(out) :: b_out(nbx, nby)
integer :: i, j
write(*,*) "output a"
do i = 1, nax
do j = 1, nay
write(*,*) a_in(i,j)
b_out(i,j) = a_in(i,j)
end do
end do
write(*,*) "output b"
do i = 1, nax
do j = 1, nay
write(*,*) b_out(i,j)
end do
end do
end subroutine
end module mod_test
コンパイルする。
f2py -c -m test test.f90 --quiet
すると、test.cpython-38-darwin.so
のようなDLLが作成される。
これを以下のようにpython上で読み込み、使用することができる。
import numpy as np
import test
a = np.array([[1,2],[3,4]])
b = test.mod_test.test_sub(a,2,2,3,3)
print(b)
ここで、読み込まれたsubroutineのヘルプを確認してみる。
まずは ?
を用いて確認。
test.mod_test.test_sub?
Call signature: test.mod_test.test_sub(*args, **kwargs)
Type: fortran
String form: <fortran object>
Docstring:
b_out = test_sub(a_in,nbx,nby,[nax,nay])
Wrapper for ``test_sub``.
Parameters
----------
a_in : input rank-2 array('d') with bounds (nax,nay)
nbx : input int
nby : input int
Other Parameters
----------------
nax : input int, optional
Default: shape(a_in, 0)
nay : input int, optional
Default: shape(a_in, 1)
Returns
-------
b_out : rank-2 array('d') with bounds (nbx,nby)
次に、help()
を用いて確認。
help(test.mod_test.test_sub)
Help on fortran object:
class fortran(object)
| Methods defined here:
|
| __call__(self, /, *args, **kwargs)
| Call self as a function.
|
| __repr__(self, /)
| Return repr(self).
help()
と?
は同じものを返すと思っていたが、ここでは違う模様。引数の取り方やオプションは?
でないと確認できない模様。
引数の渡し方の注意点
Fortranでは以下の順で引数を与えた。
subroutine test_sub(a_in, nax, nay, b_out, nbx, nby)
integer, intent(in) :: nax, nay, nbx, nby
real(8), intent(in) :: a_in(nax, nay)
real(8), intent(out) :: b_out(nbx, nby)
しかし、?
で確認した際の引数の一覧は以下のようになっている。
Parameters
----------
a_in : input rank-2 array('d') with bounds (nax,nay)
nbx : input int
nby : input int
Other Parameters
----------------
nax : input int, optional
Default: shape(a_in, 0)
nay : input int, optional
Default: shape(a_in, 1)
つまり、Fortranではa_in, nax, nay, b_out, nbx, nby
の順で定義したにも関わらず、Pythonから呼び出す際にはa_in, b_out, nbx, nby, nax (optional), nay(optional)
の順になっている。
どういうこと?
以下のように並び替えが行われる模様。
- intent(out)はsubroutineであっても関数のように返り値となる。(=引数として与えるとエラー)
- intent(in)の配列を与えると、その配列の大きさは
np.shape()
を用いて自動的に設定される(optionalな引数となる)。 - optionalな引数は必須の引数より後でなければならない。そのため、optional引数は最後に。
このように、引数の順番が入れ替わるため、順番に注意して設定する必要がある。その際のヘルプは、?
を使わなければならない。
f2pyを使っていて以上の点にハマってしまいました。
また出張から帰ったらもう少し記述を充実させて再度公開しようと思います。