*********************************** * nc_util (ver 0.7) * * 2024/09/26 伊藤耕介(京都大学) * *********************************** *** 更新履歴 ***  ver. 0.4.0   ・無制限次元を使ったときに値が正しく書き込まれないバグを修正。   ・5次元の値の書き込みの際にエラーとメッセージが対応しない点を修正。  ver. 0.5.0   ・無制限次元に関するバグを修正。   ・エラーメッセージを分かりやすく。   ・nc_util_initにdefault_quietオプションを追加。   ・実行時に書き込むファイル数、次元数、変数の数が上限に達したときに、   エラーを吐いて止まるように修正。  ver. 0.5.1   ・コンパイル用のスクリプトと実際のプログラムの不一致があったので解決。  ver. 0.6.0   ・マニュアルの訂正   ・1行でファイル生成から閉じるまでを行うnc_write_onelineを追加。   ・nc_write_4d_r8, nc_write_5d_r8などで配列が正しく定義されるように修正   ・optionのstartがnc_write_4d,nc_write_5dで正しくセットされるようバグ修正  ver. 0.6.1   ・nc_syncを追加  ver. 0.6.2   ・nc_readの配列アドレスの不連続性の可能性を解消。  ver. 0.6.3   ・軽微なバグの修正  ver. 0.6.4   ・軽微なバグの修正  ver. 0.6.5   ・byte型及びinteger(1)型の追加  ver. 0.7   ・byte型, integer(1)型, char型, integer(8)型の扱いに関するバグ修正 *** nc_utilの概要 *** 伊藤が個人的に使っているNetCDFのFortran90用ラッパーです。NetCDFでは、ファイル・次元・変数をIDで管理するのですが、それをあれやこれやするのが面倒なので、IDの代わりにファイル名・次元名・変数名で管理できるようにすることを目的にしたものです。 例えば、test1.ncというNetCDFファイルに入っているxyt軸上に100×100×20のサイズで定義された変数V(単精度実数)を読み込む際には、何も使わずに愚直に書くと、 1 RETVAL = NF90_OPEN(“test1.nc”, 0, FILE_ID) 2 IF (RETVAL .NE. NF_NOERR) CALL NC_ERR_HANDLE(RETVAL,msg) 3 RETVAL = NF90_INQ_VARID(FILE_ID, “V”, VAR_ID) 4 IF (RETVAL .NE. NF_NOERR) CALL NC_ERR_HANDLE(RETVAL,msg) 5 RETVAL = NF90_GET_VAR (FILE_ID, VAR_ID, A) 6 IF (RETVAL .NE. NF_NOERR) CALL NC_ERR_HANDLE(RETVAL,msg) 7 RETVAL = NF90_CLOSE(FILE_ID) 8 IF (RETVAL .NE. NF_NOERR) CALL NC_ERR_HANDLE(RETVAL,msg) のようになります。簡単に解説すると、1行目でFILE_NAMEというをオープンしファイルIDを取得、2行目でファイルオープンが成功したかチェック、3行目でファイルIDと変数名Vを与えて変数VのIDを取得、4行目で変数のIDの取得が成功したかチェック、5行目でファイルIDと変数IDを与えて、変数Vの値をA(ここでは、100×100×20の配列と仮定)に格納し、6行目でそれが成功したかどうかをチェック、7行目でファイルをクローズし、8行目でクローズがうまくいったかチェック、という手順を経ることになります。ただし、nc_error_handleはユーザーが定義した返り値をチェックするサブルーチンであるとします。  これと同じことを、nc_utilでは 1 call nc_read_3d(“test1.nc”, ”V”, (/100, 100, 20/), A) で実現します。直観的で分かりやすいと思います。  このようなことができるのは、機能に汎用性を持たせず、NetCDFを限定的な利用方法のみを想定しているためです。また、いろいろと裏で行っている分、スピードは非常に遅くなりますし、機能に物足りなさを感じるのであればnc_utilは使わない方がいいと思いますが、初心者の方などNetCDFに慣れていない方が基本的な機能だけに絞って使うには便利でしょう。 似たようなものとして、gtool5(http://www.gfd-dennou.org/arch/gtool/gtool5.htm)やnc_utilities(http://www.ssec.wisc.edu/~paulv/Fortran90/netCDF/)など、優れたソフトウェアがあります。というか、これらのソフトウェアのほうがはっきり言って性能は格段に上ですし洗練されています。そんな中、nc_utilをあえて作成したのは、nc_util.f90というファイルをコンパイルの際に加えれば使えるというお手軽さによるところが大きいです。新たなライブラリの生成を作成したりMakefileの複雑な書き換えをすることなく、NetCDFの利用が便利になります。 <そもそも、NetCDFって?>  NetCDFは自己記述的でかつポータブルなインターフェース(計算機における手順・規則)であり、その実装を行うものです。「自己記述的」ということは、そのファイルが自身のデータに関する情報を内包しているということで、例えば、計算結果として、3次元変数Vを出力するならば、座標軸の情報や単位などを関連付けて格納できます。これに対して、GradsやNuSDaS形式ですと、軸・変数の情報を記述したテキストファイルを別途用意しておく必要があります。僕も初心者のころは、テキストファイルとして出力結果と軸情報を出しておき、それでいろいろと解析をしていたのですが、出力する内容が安定しているのであればともかく、解析したい内容や開発段階によって、形式をどんどん変えていく必要があったので、あとで非常に苦労しました。また、テキスト形式だとファイルサイズが非常に大きくなるのも問題です。一つのファイルで全ての情報を集約しておき、付加情報も与えられるというのは、NetCDFの一つの大きな利点です。  ポータブルである、ということは、NetCDFさえインストールしておけば、環境Aで生成した結果のNetCDFファイルをOSやコンパイラ、利用する言語の違う環境Bでも簡単に読み書きすることができる、ということです。たとえば、fortranの内部形式ですと、あるコンパイラを使って出力した結果をほかのコンパイラで読むことはできません。  もう一つのNetCDFの利点は、気象・海洋をはじめとする業界の標準になりつつあるという点でしょう。Gradsでも、(若干の制約はあるものの)NetCDFファイルをそのまま開くことができますし、GPhysやIDL, NCLでも扱うことができます。例えば、私の大好きなgpviewコマンド(インストールがちょっと大変ですが)を使えば、 %gpview test1.nc@V,t=10 とするだけで、t=10における変数Vの図が描けるのです。 *** nc_utilの使い方(コンパイル編)***  ここでは、NetCDF自身はインストール済みであると仮定します。そして、ライブラリ(libnetcdf.xxx)が/home/ito/local/netcdf/libにあり、netcdf.inc(fortranでnetcdfを使うときに必要となる変数を定義したもの)が/home/ito/local/netcdf/includeにあるとします。  たとえば、netcdfによる書き込みを行うsample.f90というファイルがあるとき、もとのコンパイルが、  frt -I/home/ito/local/netcdf/include sample.f90 -L/home/ito/local/netcdf/lib -lnetcdf であれば、これを  frt -I/home/ito/local/netcdf/include nc_util.f90 sample.f90 \ -L/home/ito/local/netcdf/lib -lnetcdf とするだけです。  sample.f90において、矢印で示した行を加える必要があります。  program sample use nc_util <--追加 implicit none ・・・  call nc_util_init <--追加 ・・・ use nc_utilはこれから、nc_utilというモジュールを使うよ、という宣言で、implicit noneより前に書かなくてはなりません。また、nc_util_initはnc_createやnc_writeなど、ほかのnc_util系のサブルーチンを使う前に呼び出しておかなくてはいけません。 *** 読み込みの基本 ***  call nc_read_2d("test2.nc","U",(/10,2/),Uwrk) とするだけで、test2.ncというnetcdfファイルを開き、"U"という10×2の配列サイズを持つ変数をfortran上のUwrkに読み出せます。現時点で読みだせるのは、単精度実数・倍精度実数・2バイト整数・4バイト整数・8バイト整数のみです 。それ以外の変数を読もうとするとコンパイルエラーが出るようになっています(いるはずです)。  上の設定では、NetCDFファイル上の(1,1)から(10,2)までを取得することになりますが、(1,2)から(10,2)にしたいときには、以下のように、startオプションを設定し取得開始点を指示するとともに取得サイズを変更します(Uwrk2のサイズは10×1です)。大量のデータの必要な部分だけを読み込むときには、これで高速化が図れます。  call nc_read_2d("test2.nc","U",(/10,1/),Uwrk2,start=(/1,2/)) *** 簡単書き込みサブルーチン ***  配列のサイズの分かっている1つの変数の書き込みだけであれば、nc_write_onelineを使うことができます。2次元の配列Vtmp(10,20)を、次元名x,yのVという変数として書き込むときには、  call nc_write_oneline_2d("test3.nc","V",(/"x","y"/),(/10,20/),Vtmp) とするだけで書き込めます。ただし、この方法では軸の値や属性などを記すことはできません。ちゃんとしたNetCDFデータの書き込みは以下を参照してください。 *** 書き込みの基本 ***  NetCDFファイルは、軸の情報を自身に持っているといいましたが、逆にいえば、そのことは、次元と変数の定義を明確にする必要があるということです。NetCDFには、次元や変数を定義するための定義モードと、計算結果の値を書き込んでいくデータモードがあり、データモードに入る前には、nc_enddefによって定義モードの終了を宣言しておかなくてはいけません。(nc_utilでは実装していませんが、nf90_redefを使うことによって改めてデータモードから定義モードに戻ることができます)  以下は、NetCDFファイルの書き込みを行う際の一般的な順番です。 注:読み込みと同様に、現時点で値が書き込めるのは、単精度実数・倍精度実数・2バイト整数・4バイト整数・8バイト整数のみです。属性としては、これらに加えて、文字型を書き込むこともできます。 ファイルを書き込み用にオープン 軸の定義 変数の定義 (必要ならば)単位や説明などの属性の付与 定義モードの終了を宣言 値の書き込み ファイルのクローズ  一番簡単な例として、Fortranの計算結果U_result(x軸方向に長さ100)をtest1.ncに変数Uとして保存することを考えます。上の順序に対応させて書くと、  !ファイルを作成  call nc_create("test1.nc")  ! x軸を定義  call nc_def_dim("test1.nc","x",100)  ! Uを1次元変数(単精度実数)として定義。  call nc_def_var_1d("test1.nc","U","single",(/"x"/))  ! 単位を属性として書き込む(省略可)。  call nc_put_att("test1.nc","U","units","m/s")  ! test1.ncの定義モード終了。  call nc_enddef("test1.nc")  ・・・(計算)  !データの書き込み  call nc_write_1d("test1.nc","U",U_result)  !ファイルを閉じる  call nc_close("test1.nc")  これで、出来上がりなのですが、このままでは、x軸が長さ100であることを定義しただけで、x軸の値がどのようなものであるかを書き込んだことにはなりません。1次元配列x_axisをx軸の値として書き込むには、矢印で示した2行を追加します。  !ファイルを作成  call nc_create("test1.nc")  ! x軸を定義  call nc_def_dim("test1.nc","x",100)  !x軸に値を書きこめるように定義。  call nc_def_var_1d ("test1.nc","x","single",(/"x"/))  <--追加   ! Uを1次元変数(単精度実数)だと定義。  call nc_def_var_1d("test1.nc","U","single",(/"x"/))  ! 単位を属性として書き込む(省略可)。  call nc_put_att("test1.nc","U","units","m/s")  ! test1.ncの定義モード終了。  call nc_enddef("test1.nc")  !x軸の値を出力  call nc_write_1d("test1.nc","x",x_axis)  <--追加  ・・・(計算)  !データの書き込み  call nc_write_1d("test1.nc","U",U_result)  !ファイルを閉じる  call nc_close("test1.nc")  NetCDFの便利な機能として、レコード次元の存在があげられます。これは、次元の長さを決めずに、書き込みした回数だけ次元が広がっていくというものです。レコード次元を時間方向にとれば、時間積分の決められたタイムステップごとに出力を追加していけます。いま、変数V=V(x,t)を単精度実数として書き込むことを考えます。x軸方向には長さ100,時間方向にはレコード次元とします。先ほどの手順は、  ! ファイルを作成  call nc_create("test1.nc")  ! x次元を定義  call nc_def_dim("test1.nc","x",100)  ! t次元(レコード次元)を定義  call nc_def_dim("test1.nc","t",0) ! 0はレコード次元を表す。  ! x軸に値を書きこめるように定義。  call nc_def_var_1d ("test1.nc","x","single",(/"x"/))  ! Vを二次元変数(単精度実数)だと定義。  call nc_def_var_2d("test1.nc","V","single",(/"x","t"/))  ! 単位を属性として書き込む(省略可)。  call nc_put_att("test1.nc","V","units","m/s")  ! test1.ncの定義モード終了。  call nc_enddef("test1.nc")  !軸の値の出力  call nc_write_1d("test1.nc","x",x_axis)  ・・・(計算)  ! Vのある時刻のデータを書き込む。V自身は2次元変数だが、  ! レコード次元を使っており、1次元の値V_snapshotを書き込んでいることに注意  call nc_write_1d("test1.nc","V",V_snapshot)  ・・・(計算)  ! 二回目のデータの書き込み  call nc_write_1d("test1.nc","V",V_snapshot)  ! ファイルを閉じる  call nc_close("test1.nc") これで、x方向100, 時間方向2のデータVが出来上がりました。 *** メッセージの出力に関するオプション *** nc_utilのコマンドを用いたときにはメッセージが出ますが、これを出力したくなければ、  call nc_read_2d("test2.nc","U",(/10,2/),Uwrk, quiet=”T”) などのように、quietオプション”T”を追加してください。また、nc_util_initを呼び出す際に、  call nc_util_init(default_quiet=”T”) とすると、quietのデフォルト値が”T”になります。そのあとで、個別にquiet=”F”とした場合には、”F”が選択されます。 *** nc_utilに含まれるサブルーチン ***  nc_utilには以下のサブルーチンしか含まれません。netcdfの全ての機能をサポートするものではありません。  nc_util_init:   nc_utilで用いられる変数の初期化。他のサブルーチンを使う前に呼び出す。 nc_read_1d:   1次元の変数を読み込む。 nc_read_2d:   2次元の変数を読み込む。 nc_read_3d:   3次元の変数を読み込む。 nc_read_4d:   4次元の変数を読み込む。 nc_read_5d:   5次元の変数を読み込む。 nc_get_dimlen:   軸の次元の長さを読み込む。 nc_get_att:   変数の属性を読み込む。例えば、単位や未定義値がいくつになるか。 nc_get_global_att:   全体に共通の属性を読み込む。例えば、プロジェクト名や重力加速度。 nc_create:   書き込みのため、ファイルを新たに作成する。 nc_def_dim:   書き込みのため、新たな空間座標や時間軸などの次元を定義する。 nc_def_var_1d:   書き込みのため、1次元の変数を定義する。 nc_def_var_2d:   書き込みのため、2次元の変数を定義する。 nc_def_var_3d:   書き込みのため、3次元の変数を定義する。 nc_def_var_4d:   書き込みのため、4次元の変数を定義する。 nc_def_var_5d:   書き込みのため、5次元の変数を定義する。 nc_put_att:   変数の属性を書き込む。例えば、単位や未定義値がいくつになるか。 nc_put_global_att:   全体に共通の属性を書き込む。例えば、プロジェクト名や重力加速度。 nc_enddef:   書き込み時の定義モード終了宣言。これをしないと、nc_write_xxが使えない。 nc_write_0d:   スカラー量を書き込む。現時点では、レコード次元(以下参照)のみ。 nc_write_1d:   1次元の変数を書き込む。 nc_write_2d:   2次元の変数を書き込む。 nc_write_3d:   3次元の変数を書き込む。 nc_write_4d:   4次元の変数を書き込む。 nc_write_5d:   5次元の変数を書き込む。 nc_write_oneline_1d:   1つの1次元の変数の書き込みに関し、ファイル生成から閉じるまで一括して行う。   (nc_create, nc_def_dim, nc_def_var, nc_enddef, nc_write, nc_closeを呼ぶ) nc_write_oneline_2d:   nc_write_oneline_1dと同じ。ただし、1つの2次元の変数を書き込む。 nc_write_oneline_3d:   nc_write_oneline_1dと同じ。ただし、1つの3次元の変数を書き込む。 nc_write_oneline_4d:   nc_write_oneline_1dと同じ。ただし、1つの4次元の変数を書き込む。 nc_write_oneline_5d:   nc_write_oneline_1dと同じ。ただし、1つの5次元の変数を書き込む。 nc_close:   書き込みを終了する。 nc_sync:   ファイルをデータファイルに同期する。プログラムの途中でもファイルに書き出しをしておきたいときに呼び出す。 *** サブルーチンの引数(オプション引数は除く) ***  nc_util_init nc_read_1d(file,varname,(/n1/),V_1D)   in: file, varname, n1   out: V_1D nc_read_2d(file,varname,(/n1,n2/),V_2D)   in: file, varname, n1, n2   out: V_2D nc_read_3d(file,varname,(/n1,n2,n3/),V_3D)   in: file, varname, n1, n2, n3   out: V_3D nc_read_4d(file,varname,(/n1,n2,n3,n4/),V_4D)   in: file, varname, n1, n2, n3, n4   out: V_4D nc_read_5d(file,varname,(/n1,n2,n3,n4,n5/),V_5D)   in: file, varname, n1, n2, n3, n4, n5   out: V_5D nc_get_dimlen(file,dimname,DIMLEN)   in: file, dimname   out: DIMLEN nc_get_att(file,varname,attname,ATTVAL)   in: file, varname, attname   out: ATTVAL nc_get_global_att(file,globalattname,ATTVAL)   in: file, globalattname   out: GLOBALATTVAL nc_create(file)   in: file nc_def_dim(file,dimname,dimlen)   in: file, dimname,dimlen nc_def_var_1d(file,varname,vartype,dimnames_1d)   in: file, varname, vartype, dimnames_1d nc_def_var_2d(file,varname,vartype,dimnames_2d)   in: file, varname, vartype, dimnames_2d nc_def_var_3d(file,varname,vartype,dimnames_3d)   in: file, varname, vartype, dimnames_3d nc_def_var_4d(file,varname,vartype,dimnames_4d)   in: file, varname, vartype, dimnames_4d nc_def_var_5d(file,varname,vartype,dimnames_5d)   in: file, varname, vartype, dimnames_5d nc_put_att(file,varname,attname,attval)   in: file, varname, attname, attval nc_put_global_att(file,globalattname,globalattval)   in: file, globalattname, globalattval nc_enddef(file)   in: file nc_write_0d(file,varname,v_0d)   in: file, varname, v_0d nc_write_1d(file,varname,v_1d)   in: file, varname, v_1d nc_write_2d(file,varname,v_2d)   in: file, varname, v_2d nc_write_3d(file,varname,v_3d)   in: file, varname, v_3d nc_write_4d(file,varname,v_4d)   in: file, varname, v_4d nc_write_5d(file,varname,v_4d)   in: file, varname, v_5d nc_write_oneline_1d(file,varname,dimnames_1d,dimlens_1d,v_1d)   in: file, varname, dimnames_1d, dimlens_1d, v_1d nc_write_oneline_2d(file,varname,dimnames_2d,dimlens_2d,v_2d)   in: file, varname, dimnames_2d, dimlens_2d, v_2d nc_write_oneline_3d(file,varname,dimnames_3d,dimlens_3d,v_3d)   in: file, varname, dimnames_3d, dimlens_3d, v_3d nc_write_oneline_4d(file,varname,dimnames_4d,dimlens_4d,v_4d)   in: file, varname, dimnames_4d, dimlens_4d, v_4d nc_write_oneline_5d(file,varname,dimnames_5d,dimlens_5d,v_5d)   in: file, varname, dimnames_d, dimlens_5d, v_5d nc_close(file)   in: file nc_sync(file)   in: file また、上に出てきた引数の意味は以下のとおりです。  file(character(LEN=255))   ファイル名  varname(character(LEN=255))   変数名  n1,n2,n3,n4,n5(integer)   1次元目、2次元目、3次元目、4次元目、5次元目の長さ  v_0d(integer(2 or 4 or 8) or real(4 or 8))   スカラー量  v_1d(integer(2 or 4 or 8) or real(4 or 8))(n1)   1次元配列  v_2d(integer(2 or 4 or 8) or real(4 or 8))(n1,n2)   2次元配列  v_3d(integer(2 or 4 or 8) or real(4 or 8))(n1,n2,n3)   3次元配列  v_4d(integer(2 or 4 or 8) or real(4 or 8))(n1,n2,n3,n4)   4次元配列  v_5d(integer(2 or 4 or 8) or real(4 or 8))(n1,n2,n3,n4,n5)   5次元配列  attname(character)   属性名  attval (character or integer(2 or 4 or 8) or real(4 or 8))   属性の値  globalattname(character)   グローバル属性名  globalattval (character or integer(2 or 4 or 8) or real(4 or 8))   グローバル属性の値  dimname(character)   次元名  dimlen(integer(4))   次元の長さ  dimlens_1d(integer(4)) (1)   次元の長さを含む配列  dimlens_2d(integer(4)) (2)   次元の長さを含む配列  dimlens_3d(integer(4)) (3)   次元の長さを含む配列  dimlens_4d(integer(4)) (4)   次元の長さを含む配列  dimlens_5d(integer(4)) (5)   次元の長さを含む配列 vartype(character)   変数の型(但し、以下のうちいずれかであること。   single, SINGLE, float, FLOAT, real, REAL, real8, REAL8, double, DOUBLE,   short, SHORT, int, INT, integer, INTEGER) dimnames_1d(character)(1)   1次元変数が持つ1つの次元 dimnames_2d(character)(2)   2次元変数が持つ2つの次元 dimnames_3d(character)(3)   3次元変数が持つ3つの次元 dimnames_4d(character)(4)   4次元変数が持つ4つの次元 dimnames_5d(character)(5)   5次元変数が持つ5つの次元 *** その他の注意 *** ・現時点では、最大ファイル数=400, 最大次元数=100,変数の最大数=200を仮定しています。これより大きい値が必要な場合には、nc_util.f90のnc_maxfile, nc_maxdim, nc_maxvarをそれぞれ編集してください。 ・nc_utilでは、real(4)を単精度、real(8)を倍精度、integer(2)を2バイト変数、integer(4)を4バイト変数、integer(8)を8バイト変数だと仮定していますが、厳密にいうと、FortranのISO規格ではこれについて規定されていないことになっています。普通に使う分には気にする必要はないと思いますが、ごく一部のFortranコンパイラ(かつてのg77のFortran90モード)では、ひねくれた設定になっています。 ・何か問題があれば、itokosk(あっと)sci.u-ryukyu.ac.jpまでご連絡ください。