CNSをスクリプトで便利に!
単純なスクリプト
CNS用のスクリプトを作成してみます。
CNSをスクリプトで使用する際は気を付けなければならない点を少し。
- ユーザーがCtrl-Cを押したときの割り込み
- インプットファイルのエラーにより停止したとき
作成したスクリプト
この時の状態を考慮して作成する必要があります。それで作成してみたスクリプトを載せてみます。
- runcns
1: #!/bin/bash 2: TASK=$1 3: 4: if *1 5: then 6: echo "usage: runcns <taskname>" 7: exit 1 8: fi 9: 10: if [ ! -e $TASK.inp ]; then echo "$TASK.inp does not exist"; exit 1; fi 11: 12: # Run CNS 13: cns < ${TASK}.inp | tee ${TASK}.log 14: 15: # Check CNS termination code 16: RES=$? 17: if *2 # User interruption (Ctrl-C) 18: then 19: echo "Task [$TASK] terminated by user interruption ($RES)" 20: exit $RES 21: elif *3 # Other errors 22: then echo "Task [$TASK] aborted on error ($RES)" 23: exit $RES 24: fi 25: 26: #Check CNS error 27: grep -E "^ ABORT " ${TASK}.log > /dev/null 28: if *4 29: then 30: echo "Task [$TASK] stopped on CNS error" 31: exit 2 32: fi
使いかた
% runcns refine
refineの部分はタスク名で、インプットファイルの.inpを除いた部分が入ります。つまりgenerate.inpならgenerateです。
スクリプト解説
(15-24行目)CNSではCtrl-Cにより停止したときに終了ステータス130を返すようなので、その場合は、ユーザーにより停止された、とメッセージを出力して停止します。その他のエラーの場合は終了ステータスを表示して停止します。
(26-32行目)インプットファイルのエラーで停止した場合でもCNSは正常終了ステータス(0)を返すため、ログファイル中にABORTから始まる行が出力されていないかを確認します。その行があれば、インプットファイルに不備があるのでその旨を出力し、終了ステータスとして2を返すようにしています。これにより、CNSに引き続いて処理を行う際にエラーによる停止を行うことができます。
インプットファイルの作成
CNSで新しくタスクを実行するときに案外面倒なのがインプットファイルです。例えば、minimize.inpを使っていてその値を新たにbindividual.inpで使いたいような時に通常ならbindividual.inpをコピーしてその中の格子定数を書き換えて・・・という作業が必要になりますが、このスクリプトでは基となるインプットファイルから空間群、格子定数を取得し、新しいインプットファイルに埋め込みます。
今回はPerlを使って作ってみたalpha版です。まだあまり使い込んでないのでバグがあるかもしれません。後はCVファイルとかそのあたりのパラメータもコピーしてもいいかなと思ってますがとりあえず。
-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
#!/usr/bin/perl # CNSのインプットファイル作成スクリプト Ver.0.01 # by Nob-rin. last modified on 2009-05-18 use strict; use File::Find; my $TARGET = ""; # 対象となる新しいinpファイル my $SEARCH = ""; # 検索対象inpファイル名 my $NEWFILE = ""; # 作成するinpファイル名 my @KEYS = qw/sg a b c alpha beta gamma/; # 読み取るパラメータ main(); sub main { my ($refinp,$inp) = @ARGV; $ENV{CNS_SOLVE} || die "CNS_SOLVE not set\n"; $inp =~ s/\.inp$//; $SEARCH = $inp; -r $refinp || die "Can't open $refinp [$!]\n"; $inp || die "Target name must be set\n"; # ファイルの検索 find(\&wanted_handler,"$ENV{CNS_SOLVE}/inputs/"); $TARGET || die "Can't find $inp\n"; # パラメータを読み取って埋め込む my $cell = get_parameters($refinp); foreach my $key (@KEYS) { exists($cell->{$key}) || die "'$key' is missing\n" } set_parameters($cell,$TARGET); } sub get_parameters { # パラメータを読み取る my $inp = shift; my $cell = {}; open(my $fh,$inp) || die "Can't open $inp [$!]\n"; while(my $line = <$fh>){ foreach my $key (@KEYS) { if($line =~ /^{===>} $key=(.+);/){ $cell->{$key} = $1; last } } } close $fh; return $cell; } sub set_parameters { # パラメータを埋め込んでファイルを書き出す my ($cell,$inp) = @_; -e $NEWFILE && die "$NEWFILE already exists. Abort\n"; open(my $fh,$inp) || die "Can't open $inp [$!]\n"; open(my $out,">$NEWFILE") || die "Can't create $NEWFILE [$!]\n"; while(my $line = <$fh>){ foreach my $key (keys(%$cell)) { if($line =~ /^{===>} $key=/){ $line = "$&$cell->{$key};\n" } } print {$out} $line; } close $fh; close $out; } sub wanted_handler { # Find処理のハンドラ if($_ eq "${SEARCH}.inp"){ $TARGET = $File::Find::name; $NEWFILE = $_; } }
使い方
% createinp minimize.inp bindividual
この場合、minimize.inpから空間群、格子定数を取得して$CNS_SOLVE/inputs以下からbindividual.inpを検索し、値を埋め込んで現在のディレクトリにbindividual.inpを作成します。