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


この記事はFortranからPythonを扱う方法であるforpyを解説する記事の第三弾です。
FortranからPythonを使いたい!:forpy
Fortranで機械学習がしたい:FortranからのPyTorchの利用
が以前の記事です。

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

バージョン

  1. gcc version 11.1.0 (Homebrew GCC 11.1.0)
  2. mac OS 10.14.6

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

ファイル書き込み

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

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というファイルが書き出されます。

ファイル読み込み

ファイル読み込みは

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の配列が手に入りました。

全体

全体のコードは

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

こんな感じになります。