execute_command_line からの python 実行時に RAM Disk 利用
昔、Fortran 2003 で導入された execute_command_line の利用例として、Fortran の中で Python スクリプトを書いてファイル出力した後、それを実行すると言うことをやりました。
その時は Fortran と Python でのデータのやり取りを、直接スクリプト内に書き込む方式でやりましたが、numpy があれば Fortran 形式のバイナリファイルを読み書きできるので、ファイルを介したデータやり取りが比較的効率的にできます。この時、Linux だとお仕着せの RAM Disk があると言うことを AI に教えられたので、それを利用してみることにします。MacOS / Windows には残念ながらデフォルト状態で使える仕組みはないようです。
この方式は、一旦外部ファイルに書き出すのでダサい感はあるのですが、DLL とか Link path とかを気にしなくていいので、コロコロ環境が変わる Python に対するロバスト性というか、打たれ強さがあるところが利点です。また、スクリプトを内部生成するので、ファイルが Fortran プログラムだけで済むことも気楽です。ただ write 文で書き出すのが色々だるいところがありますが、今は AI 小僧に書かせればいいので昼下がりの気だるさ位で済みます。
実行結果
配列を外部ファイルに書き出して、Python に処理させた後、また配列に読み込みます。
(pyfort) $ ./a.out RUN: python /dev/shm/pyfor.py /dev/shm/fort_in.bin /dev/shm/fort_out.bin 10 Python read: [ 1. 2. 3. 4. 5. 6. 7. 8. 9. 10.] Python exit status = 0 Fortran got back: 10.500000000000000 20.500000000000000 30.500000000000000 40.500000000000000 50.500000000000000 60.500000000000000 70.500000000000000 80.500000000000000 90.500000000000000 100.50000000000000
ソース・プログラム
Linu ではファイル位置を /dev/shm 下におくと RAM Disk が使われるそうなので、少しだけ外部ファイル経由が軽くなります。WSL2 だとそれで動くけれど、メモリ管理の複雑さから色々微妙な模様です。
なお CLOSE 文に status = 'DELETE' 属性をつけることで、テンポラリのデータファイルや Python スクリプトを消去しています。
program pytest use iso_fortran_env, only : real64 implicit none integer, parameter :: n = 10 real(real64) :: arr(n), arr2(n) integer :: i, u, ios, exitstat character(len=*), parameter :: fin = '/dev/shm/fort_in.bin' character(len=*), parameter :: fout = '/dev/shm/fort_out.bin' character(len=*), parameter :: pys = '/dev/shm/pyfor.py' character(len=512) :: cmd do i = 1, n arr(i) = real(i, real64) end do !--- make data file open(newunit=u, file=fin, access='stream', form='unformatted', & status='replace', action='write', iostat=ios) if (ios /= 0) stop 'cannot open input binary' write(u) arr close(u) !--- generate Python script open(newunit=u, file=pys, status='replace', action='write', iostat=ios) if (ios /= 0) stop 'cannot open python script' write(u,'(A)') 'import sys' write(u,'(A)') 'import numpy as np' write(u,'(A)') '' write(u,'(A)') 'fin = sys.argv[1]' write(u,'(A)') 'fout = sys.argv[2]' write(u,'(A)') 'n = int(sys.argv[3])' write(u,'(A)') '' write(u,'(A)') 'arr = np.fromfile(fin, dtype=np.float64, count=n)' write(u,'(A)') 'print("Python read:", arr)' write(u,'(A)') 'arr = arr * 10.0 + 0.5' write(u,'(A)') 'arr.tofile(fout)' close(u) !--- Python Execution ! cmd = 'python ' // pys // ' ' // fin // ' ' // fout // ' 10' print *, 'RUN: ', trim(cmd) call execute_command_line(trim(cmd), exitstat=exitstat) print *, 'Python exit status = ', exitstat if (exitstat /= 0) stop 'python failed' !--- read results open(newunit=u, file=fout, access='stream', form='unformatted', & status='old', action='read', iostat=ios) if (ios /= 0) stop 'cannot open output binary' read(u) arr2 print *, 'Fortran got back:' print *, arr2 ! delete files close(u, status = 'delete') open(newunit = u, file = fin) close(u, status = 'delete') open(newunit = u, file = pys) close(u, status = 'delete') end program pytest

