Fortranからnumpy配列を読み書きしたい！: forpyの利用その3

この記事はFortranからPythonを扱う方法であるforpyを解説する記事の第三弾です。
[FortranからPythonを使いたい！:forpy](https://qiita.com/cometscome_phys/items/5b4347a48fb25b52e472)
[Fortranで機械学習がしたい：FortranからのPyTorchの利用](https://qiita.com/cometscome_phys/items/54adfb82410f1a3c0831)
が以前の記事です。

今回は、Fortranで作った配列をnumpyの配列にして、その配列をnpy形式で読み書きする方法です。
npy形式とは、Pythonでnumpyの配列をそのまま保存する場合のフォーマットです。

## バージョン
1.  gcc version 11.1.0 (Homebrew GCC 11.1.0) 
2.  mac OS 10.14.6

で試しました。
forpyの使い方は以前の記事をみてください。

## ファイル書き込み
まず、ファイルの書き込みです。こんな感じになります。

```fortran
subroutine test()
    use forpy_mod
    use,intrinsic::iso_fortran_env
    implicit none
    integer(int32)::ierror
    real(real64),allocatable::x(:)
    integer(int32)::N
    type(ndarray)::x_numpy,x2_numpy

    type(module_py) :: np
    type(tuple) :: args

    N = 10
    allocate(x(1:N))
    call random_number(x)
    write(*,*) "x = ",x

    
    ierror = import_py(np, "numpy")

    ierror = ndarray_create(x_numpy,x)
    ierror = print_py(x_numpy)

    ierror = ndarray_create_nocopy(x2_numpy,x)
    x(1) = 100d0
    ierror = print_py(x2_numpy)

    ierror = tuple_create(args, 2)
    ierror = args%setitem(0, "test")
    ierror = args%setitem(1, x_numpy)

    ierror = call_py_noret(np,"save",args)

    call x_numpy%destroy
    call x2_numpy%destroy

end subroutine
``` 

ここで、```ndarray_create```は新しいnumpy配列を作り、```ndarray_create_nocopy```はコピーなしでFortranの配列とnumpy配列を紐つけるものです。紐ついているために、Fortranの配列をいじるとnumpyの配列も変わっていることがわかります。このコードではtest.npyというファイルが書き出されます。

# ファイル読み込み
ファイル読み込みは

```fortran
subroutine test2()
    use forpy_mod
    use,intrinsic::iso_fortran_env
    implicit none
    integer(int32)::ierror
    type(module_py) :: np
    type(tuple) :: args
    type(object) :: arr
    real(real64),dimension(:), pointer ::x
    type(ndarray)::x_numpy
    integer(int32)::N

    N = 10

    ierror = import_py(np, "numpy")
    ierror = tuple_create(args, 1)
    ierror = args%setitem(0, "test.npy")

    ierror = call_py(arr,np,"load",args)

    ierror = ndarray_create_empty(x_numpy, [N], dtype="float64")
    !ierror = cast(arr,x_numpy)
    ierror = cast(x_numpy,arr) !arrの内容をx_numpyにcast
    ierror = x_numpy%get_data(x)

    write(*,*) "load"
    write(*,*) x

end subroutine
```
です。numpy配列をFortran配列にする場合には、```call_py```で返ってきたPythonオブジェクトを一旦castでnumpy配列にしてから、```get_data```をしなければならないことに注意してください。また、Fortranの配列にはpointer属性をつけてください。これで、Fortranの配列が手に入りました。

# 全体
全体のコードは

```fortran
subroutine test()
    use forpy_mod
    use,intrinsic::iso_fortran_env
    implicit none
    integer(int32)::ierror
    real(real64),allocatable::x(:)
    integer(int32)::N
    type(ndarray)::x_numpy,x2_numpy

    type(module_py) :: np
    type(tuple) :: args

    N = 10
    allocate(x(1:N))
    call random_number(x)
    write(*,*) "x = ",x

    
    ierror = import_py(np, "numpy")

    ierror = ndarray_create(x_numpy,x)
    ierror = print_py(x_numpy)

    ierror = ndarray_create_nocopy(x2_numpy,x)
    x(1) = 100d0
    ierror = print_py(x2_numpy)

    ierror = tuple_create(args, 2)
    ierror = args%setitem(0, "test")
    ierror = args%setitem(1, x_numpy)

    ierror = call_py_noret(np,"save",args)

    call x_numpy%destroy
    call x2_numpy%destroy

end subroutine

subroutine test2()
    use forpy_mod
    use,intrinsic::iso_fortran_env
    implicit none
    integer(int32)::ierror
    type(module_py) :: np
    type(tuple) :: args
    type(object) :: arr
    real(real64),dimension(:), pointer ::x
    type(ndarray)::x_numpy
    integer(int32)::N

    N = 10

    ierror = import_py(np, "numpy")

    ierror = tuple_create(args, 1)
    ierror = args%setitem(0, "test.npy")

    ierror = call_py(arr,np,"load",args)

    ierror = ndarray_create_empty(x_numpy, [N], dtype="float64")
    !ierror = cast(arr,x_numpy)
    ierror = cast(x_numpy,arr) !arrの内容をx_numpyにcast
    ierror = x_numpy%get_data(x)

    write(*,*) "load"
    write(*,*) x
end subroutine


program main
    use forpy_mod
    use,intrinsic::iso_fortran_env
    implicit none
    integer(int32)::ierror

    ierror = forpy_initialize()
    call test()
    call test2()
end program
```
こんな感じになります。