Fortranのバイナリ出力データ(unformatted)をPythonで読む

Fortranで出力したunformatted形式のデータをPythonで読む方法です。 scipyにFortranFileというものがあるので、それを使ってもできます。

scipy.io.FortranFile — SciPy v0.15.0.dev Reference Guide

でもこれソース読めばわかりますが、numpyのfromfileって関数を利用してるだけです。

numpy.fromfile — NumPy v1.8 Manual

fromfileで直接扱ったほうが応用も効くのでこれでやります。環境依存な部分もあるかもしれませんので注意してください。

今回読み込むデータは次のようなFortranソースコードで出力されたとします。

program main
    integer :: a, i,j
    real(8) :: b, arr(4,4)
    a = 4
    b = 3.14
    forall(i=1:4,j=1:4)
        arr(i,j) = 10*i+j
    end forall
    open(20, file="output.dat", form="unformatted")
    write(20) a, b, arr
    close(20)
end program

aは32bit整数、bは64bit浮動小数点数、arrは4x4の2次元配列です。こうすると、output.datというファイルには

f:id:ignisan:20140530225754p:plain

という感じでデータが並びます。データの先頭と末尾にwrite文で出力したデータサイズ、今回の場合は

変数aのサイズ(4)+bのサイズ(8)+arrのサイズ(4x4x8) = 140バイト

がSize information(正式な呼び方は知りません)と書いたところに入ります。この領域自体は4バイトです。ファイルに追記していく場合でもwrite分のたびに、サイズが出力されます。

Python側ですが、fromfileのドキュメントを読めば予想がつくかもしれません。こんなかんじになります。

import numpy as np

head = ("head","<i")
tail = ("tail","<i")

dt = np.dtype([head, ("a","<i"), ("b","<d"), ("arr","<16d"), tail])
fd = open("output.dat","r")
chunk = np.fromfile(fd, dtype=dt, count=1)
print "a:",chunk[0]["a"]
print "b:",chunk[0]["b"]
arr = chunk[0]["arr"].reshape((4,4),order="F")
print "arr:",arr

dtypeでデータ構造を指定しています。"<"はリトルエンディアンです。ビッグエンディアン環境で出力した場合は代わりに">"を使います。つまり

dtype 意味
<i トルエンディアンの整数
<d トルエンディアンの浮動小数点数
<16d トルエンディアンの浮動小数点数16個

という意味です。2次元配列とかの指定方法はないので、配列の総数を指定します。データを読み込んだ後、reshapeを使って2次元配列に整形します。order="F"がポイントです。いわゆるC言語系とFortranでは多次元配列の並び方が違うので。

fromfileでcount=1としているのは読み込みをループしないという意味です。今回はwrite文が1回しかないので書かなくても同じですが。複数回write文を呼び出した場合はcountを読み込みたい回数に変更します。chunk[1]に2回目のwrite文で出力したデータが入ります。

これでFortranのデータは読めるようになったので、Pythonでデータを解析してmatplotlibで可視化するとか簡単にできますね!