f2pyの使い方：配列の受け渡しに関する注意点

# 概要

f2pyを使用するとFortranで作成したsubroutineをPythonから実行することができる。しかし、subroutineの実行時に、特に引数に配列のが含まれる場合、引数が省略されたり順番が入れ替わる場合がある。そのため、本記事では配列を受け渡す際の引数についてまとめる。

f2pyを昨日使い始めたばかりの素人なので、間違い等もあると思います。コメント等で教えていただけると助かります。

この記事は書きかけです。出張から帰ってきたらもう少しまともに書き直します。

# 環境

- Python 3.8.9
- f2py 1.22.4
- numpy 1.22.4

# f2pyとは

Fortranで書いたサブルーチンをPython動かすためのパッケージ。


# Fortranソース

以下のような(意味のない)コードを用意した。

```Fortran:test.f90
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
```

コンパイルする。

```zsh
f2py -c -m test test.f90 --quiet
```
すると、```test.cpython-38-darwin.so```のようなDLLが作成される。
これを以下のようにpython上で読み込み、使用することができる。

```ipython3: test.ipynb
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?
```
```docstring
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を使っていて以上の点にハマってしまいました。
また出張から帰ったらもう少し記述を充実させて再度公開しようと思います。