Bluepostech

まじめなことを書きます

【python】fortranのnamelistで書かれた設定ファイルを読み込む(f90nml)

以前fortranのnamelistという機能を紹介した。

数字を羅列しただけの平文ファイルとちがってnamelistはfortran独自のものなので、ほかの言語で読み込む場合は何らかの工夫が必要になる。

fortranで計算した結果をpythonのmatplotlibでグラフ化したい」というような場合は多々あるだろう。pythonではそのために「f90nml」というライブラリが用意されている。

インストールについてはpipが使える環境であれば簡単にできる。またgit cloneしてインストールすることもできる。インストールについては以下を参照されたい。

f90nml 0.2.1 : Python Package Index

ただしこのドキュメントには1ヶ所間違いがある。管理者権限のないユーザでも--userをつければインストールできるとあるが、オプションをつける位置が間違っており、正しくは以下のようにする必要がある。

 python setup.py install --user

f90nmlではreadというメソッドでnamelistをリストとして読み込み、writeというメソッドで出力できる。

具体的な使い方として、前回の記事で使ったparams.inのhogeというリストのyyという要素を取得するには、次のようにする。

 import f90nml
params=f90nml.read('params.in')
yy=params['params']['yy']

 

【fortran】設定値を別ファイルで与える(namelist)

大規模計算プログラムには大量の設定すべきパラメータがある。物理定数のように内部で与えておけばよいものもあるが、頻繁に変えて実行するものもあり、内部で与えるとその都度コンパイルしなければならず効率が悪い。

簡単に思いつくのは、「平文のまま設定値を順番に書き下してread文で読ませる」という方法だ。しかしこれだと設定ファイルが数値(時々文字列や論理値)の羅列になってしまい、可読性に欠ける。

そこで、fortranにはnamelistという機能がある。これはソースファイルとは別の設定ファイルに決まった書き方で書いた設定値を読み書きできる機能で、これを使えば比較的わかりやすい書き方で設定値を与えることができる。

使うには、まずは実行コード側でnamelistを定義する必要がある。これにはnamelist文を使って次のようにする。

 namelist/hoge/ xx,yy,zz

ただしこの文の前で変数xx、yy、zzがそれぞれ定義されている必要がある。*1設定ファイルとはいえ別ファイルなのでopenで開いてreadで読む必要があり、writeで書き出すこともできる。そのためread、writeにはnmlというパラメータが準備されている。

一方で、設定ファイルのほうは次のように記述する。

&hoge
xx=1d0,
yy=2,
zz='hoge',
/
!

注意しなければならないのは、「最後のスラッシュのあとに改行が必要」ということである。どういうわけかgfortranではこれがないと「End of file」というエラーが出るようだ。

ここではhogeというリストひとつしか記述していないが、ひとつの設定ファイルに複数個のリストを書くこともできる。この場合リスト単位で読み書きができる。

まとめて、上記の設定ファイルを「params.in」としてそれを読みとって「out.in」という名前で保存するプログラムを書くと、次のようになる。

program main
  implicit none
  double precision :: xx
  integer :: yy
  character :: zz
  namelist/hoge/xx, yy, zz

  open(10,file='params.in')
  read(10,nml=hoge)

  open(11,file='out.in')
  write(11,nml=hoge)

  close(10)
  close(11)
end program main

 

*1:implicit noneが適用されている場合。fortranプログラミングでは変数の書き間違いによるバグを防ぐためにimplicit noneは強く推奨される。

【bash】ワイルドカードが使えないコマンドでワイルドカードを使う

ワイルドカードとは、複数のファイルを一気に指定するのに使う記号で、たとえば任意の文字列を表す「*」や任意の1文字を表す「?」が代表的だ。たとえばカレントディレクトリのjpgファイルをpictureというディレクトリへ移動するときは、次のようにする。

mv *.jpg picture

しかし一部のコマンドではワイルドカードを使うことができない。たとえばlameやps2pdfではワイルドカードは使えない。「カレントディレクトリのmp3ファイルをすべてデコードする」といった場合は困る。

こういうときは、doループで処理することができる。doループをワンライナーで使う方法は以前紹介した。

 これとバッククオート(囲った内部のコマンドの結果を表す)を用いて次のようにすれば、lsとワイルドカードで指定したカレントディレクトリ下のファイルを処理することができる。

for file in `ls *.mp3` ; do lame --decode $file ; done

これを改行してスクリプトとして書くと以下のようになる。

for file in `ls *.mp3`
do
lame --decode $file
done