読者です 読者をやめる 読者になる 読者になる

Fortranで出力したデータを、call system() とシェルスクリプトを使ってその場ですぐに xz で圧縮し、元データは削除する

まえがき

前にも書いたかもだけど忘れそうなのでメモしとく。

まず Fortran 側で FV_00001.xyz, ...(グリッド)とか FV_00001.func, ...(流れ場)とか FV_00001.q, ...(時刻)といったファイルを出力している。これらは流れの可視化用のデータで、そのままだとでかい。計算機からローカルへの転送時間を短縮したいというのもあるけど、それ以上に、計算機自体の/homeディレクトリの節約のために、計算を続けながら、その場ですぐに圧縮したい。圧縮には xz を使ってるけど、お好みで gzip なり bzip2 なりにしてください。

前に書いた
システムコールとシェルスクリプトを利用して、Fortranのコードから「ディレクトリがなければ作る」を実行 - dynamicsoarの日記

natures flyers: Fortran で call system() するときに変数 (variable) を渡す方法
も参照。

注意点

Fotran 側でやるのは「シェルスクリプトのコール」までであって、圧縮作業は別のプロセス(ジョブ)になる。なので、特に OpenMP(というか並列計算)してるときは、圧縮のためにスレッドを使うので、ファイルサイズによっては流れ場計算の方が多少遅くなる可能性がある。なので、/home のサイズに余裕があるならこんなことしないで、計算終わってから一気に圧縮した方が流れ場計算は速い気がする(計算が全部終わってから、全部のファイルを圧縮するスクリプトをコールすればよい)。

Fortran側のコード

変数の意味

n_file_FV
ファイルの番号(連番)で、保存するごとに増えてくやつ
if_compress_FV_grid, if_compress_FV_flow, if_compress_FV_q
いずれも設定ファイルから読み込むパラメタ (integer) で、0, 1, 2 のいずれかを取る。意味は、0=圧縮しない, 1=圧縮してオリジナルデータは残す, 2=圧縮してオリジナルデータは削除。本来の目的からいって、僕は基本的には 2 を使ってるけど、グリッドの位置調整とかには 0 が便利だし、容量節約よりも計算機上での可視化&後でのデータ転送高速化を目的として 1 を使うこともある。

グリッド保存用サブルーチン

  !! ファイル名(パスを含む)を設定
  write(out_grid,"('./output/FV_grid/FV_',i5.5,'.xyz')") n_file_FV
  <実際の出力作業は省略>

  !! 圧縮
  write(command,"('sh ./src/compress_output_file.sh ',i1,1x,'grid',1x,i5.5)") if_compress_FV_grid, n_file_FV
  call system(command)

流れ場保存用サブルーチン

function ファイルなんだけど僕らは伝統的に q ファイルでなくこっちに流れ場を書いてるので。

  !! ファイル名(パスを含む)を設定
  write(out_func,"('./output/FV_func/FV_',i5.5,'.func')") n_file_FV
  <実際の出力作業は省略>

  !! 圧縮
  write(command,"('sh ./src/compress_output_file.sh ',i1,1x,'func',1x,i5.5)") if_compress_FV_func, n_file_FV
  call system(command)

その他データ(自分の場合は時刻のみ)保存用サブルーチン

  !! ファイル名(パスを含む)を設定
  write(out_q,"('./output/FV_q/FV_',i5.5,'.q')") n_file_FV
  <実際の出力作業は省略>

  !! 圧縮
  write(command,"('sh ./src/compress_output_file.sh ',i1,1x,'q',1x,i5.5)") if_compress_FV_q, n_file_FV
  call system(command)

いま考えたらこれら3つのサブルーチンはまとめるべきだな…ほとんど同じなんだから。まぁとりあえずこのまま書いておく。

シェルスクリプト側のコード

シェルスクリプトbash以外は知らないが)ではイコールの前後に半角スペースを入れたらダメなので注意。参考:bashの変数代入の=の前後にスペースを入れてはいけない理由 - mollifier delta blog

compress_output_file.sh

これが Fortran の call system() で呼ばれる。引数を3つ取る。if_compress_FV_grid/func/q と、ファイルの種類と、n_file_FV の3つ。
そのまま functions.sh という別のファイルにある compress_output_file という関数に全部横流しする(だけ)。

#!/bin/bash

## 関数の読み込み
. ./src/functions.sh

compress_output_file $1 $2 $3

functions.sh

compress_output_file の部分のみ抜粋。他にもいろいろな関数をまとめておいている。

#!/bin/bash

#// if_compress: 0=no compress; 1=compress only; 2=compress & delete original file
#// kind: either one of: "grid", "func", or "q"
#// FVnum: FV number(Fortran側でいう n_file_FV)
#// option: xz のオプション。要は -k を使うかどうか
#//  参考: http://shellscript.sunone.me/case.html
compress_output_file()
{
  # 引数を変数にセット
  if_compress=$1
  kind=$2
  FVnum=$3
  
  ## if_compress に応じて、圧縮の際の option をセット
  case ${if_compress} in
    0 ) return ;; ## 何もしないで関数を抜ける
    1 ) option="-f -k" ;; ## 圧縮。元ファイルは残す
    2 ) option="-f" ;;   ## 圧縮 & 元ファイル削除
    * ) "Something's wrong. Check [compress_output_file] in ./src/functions.sh" ;; ## * は case default の意味
  esac
  
  ## ファイルの種類に応じた圧縮作業
  case ${kind} in
    "grid" )          xz ${option} ./output/FV_${kind}/FV_${FVnum}.xyz & ;; # {kind}はgridなんだけど拡張子はxyzなので場合分けしてる。
    "func" | "q" )    xz ${option} ./output/FV_${kind}/FV_${FVnum}.${kind} & ;; # | は or の意味
    * ) "Something's wrong. Check [compress_output_file] in ./src/functions.sh" ;;
  esac
}