NCLスクリプトの書き方に関する説明をまとめました。 基本構成 †とりあえずNCLスクリプトの中身を見てみよう。 以下のスクリプトはhttp://www.ncl.ucar.edu/Applications/xy.shtmlから引用したものである。 ところどころ省略、着色している。最初なので変数はイタリックにした。
てな感じ。 ※ resやwksの変数名は,予約語でなければ何でもよい。 実行 †hoge.nclというnclスクリプトを書いたら, $ ncl hoge.ncl とすることで実行できる。 $ ncl -h でnclコマンドのマニュアルが表示される。オプションをいくつか紹介すると,例えば, $ ncl -n hoge.ncl とすると,printを使ったときに左側に付く変数の次元(0)とか(1)とかを消すことができる。 また, $ ncl -x hoge.ncl とすると,スクリプト内のコードを表示しながら実行する。デバッグに便利。ただし,beginとendで囲まれたコードは1つのブロックとして表示されるが,メインコードでこれらをコメントアウトすれば1行ずつ確認することができる。 $ ncl -Q hoge.ncl とすると,最初に出てくる, Copyright (C) 1995-2015 - All Rights Reserved University Corporation for Atmospheric Research NCAR Command Language Version 6.3.0 The use of this software is governed by a License Agreement. See http://www.ncl.ucar.edu/ for more details. を黙らせる(Quiet)ことができる。
$ ncl year=2015 'title="NCLtips"' hoge.ncl のように代入してあげれば動く(isdefinedを使ってコマンドライン上で与えられなかった時の値を設定することもできる)。
記法 †NCLスクリプトの書き方いろいろ。スクリプトの書き方に関してはスクリプト例一覧も参照のこと。 コメントアウト †各行において;以降はコメントアウトされる。 ; この行はすべてコメント a = 1 ; a = 1は実行されるが,この文はコメント また,バージョン6.4.0以降はブロックコメントアウトが導入され,/;から;/までがコメントアウトされる。 /; ここはコメント ここもコメント ここもコメント ;/ 変数の型 †
数値型には,オリジナルの数値型であるnumericと5.2.0で追加されたenumeric(extra-numeric)のカテゴリがある。 long, ulong型はシステムのbit数でサイズ等が異なる。
logicalは論理型で,True,False,Missingのいずれかである。ただし,Missingを論理型変数あるいはAttributeに直接代入するためには,_Missingとする必要がある。 stringは文字列型,characterは文字型。 graphicは,gsn_csm_contour_map_ceなどで出力されるオブジェクトへの参照を示す変数型。 fileはサポートされているファイル形式のファイルへの参照でaddfileなどで出力される。また,groupはHDFファイル内のグループへの参照を示す型である。 listは任意の型の変数を含むリストを示す変数型でありaddfilesなどの出力がこれにあたる。 演算記号 †
5/2 ; 2 整数同士の割り算は注意 201503/100 ; 2015 でもこういう使い方は便利 5./2. ; 2.5 9%7 ; 2 9を7で割ると2余る 9%3 ; 0 2>7 ; 7 大きい方を選ぶ a = (/1,2,3/) b = (/3,2,1/) a>b ; どちらも (/3,2,3/) b>a ; 各要素で大きい方が残ります a<b ; どちらも (/1,2,1/) b<a ; 各要素で小さい方が残ります c = (/ (/1,0,0/), (/0,1,0/) /) d = (/ (/2,0/), (/1,1/), (/0,2/) /) c#d ; (/ (/2,0/), (/1,1/) /) e = "pen" f = "apple" g = "pineapple" (e+g)+(f+e) ; "penpineappleapplepen" 文字列の足し算 論理記号 †
改行 †改行は\ a = 1 + 2 + 3 + 4 + 5 + \ 5 + 6 + 7 + 8 + 9 ; a は 45 配列と代入 †NCLでは新しい変数に何かを代入すると,自動的に代入されたものの配列となる。代入は "=" を用いて, a = (/1, 2, 3/) のように行う。一度使用した変数に再度代入する場合(reassignment)は a := (/4, 5, 6, 7/) のように,":=" を使って行う。型が変わってもOK。 a = (/1, 2, 3/) a := "apple" 再代入(:=)は便利ではあるが,大きな配列を何度も再代入する場合(特にたくさんのループを回す場合)には,意図せず莫大なメモリを消費することがあるので注意すること。実際に何が起きているのかはよく分からないのだが,再代入前に使っていたメモリ領域がうまく解放されないようだ。一度再代入してしまうと元の領域を参照する術がなくなってしまうので,メモリを気にするときには面倒だがいちいちdeleteすることをオススメする。個人的には日頃から積極的に再代入を用いないでスクリプトを書くようにしている。 2次元の配列は, b = (/ (/1, 2, 3/), (/4, 5, 6/), (/7, 8, 9/) /) のように書く。 右辺の変数についているAttributeなどを左辺の変数に引き継がず,値のみを代入したい場合には, b = (/ a /) のように書く。
a = (/1, 2, 3, 4, 5, 6/) a1 = a(0) ; a1 は 1 a2 = a(3:5) ; a2 は (/4, 5, 6/) a3 = a(:4:2) ; a3 は (/1, 3, 5/) a4 = a(::-1) ; a4 は (/6, 5, 4, 3, 2, 1/) a(0) = 0 ; とすると,aは(/0, 2, 3, 4, 5, 6/)になる のようになる。もちろん多次元でも使えて, b = (/ a, a(::-1), 2*a/) b(1,:) = (/ 6, 5, 4, 3, 2, 1/) b(2,::-1) = (/12, 10, 8, 6, 4, 2/) である。変数の次元に座標が入っている場合,後述のように座標の値で指定することも可能である。
c = [/a, b/] のように記述する。変数の型や次元や大きさが異なっていても一つにまとめられる。 次元と座標 †配列の各次元に名前と座標を付けることができる。netCDFからデータを読み込むと,変数の次元に名前と格子情報がついているだろう。もし,変数sst(:,:)の0次元目がlatitudeで1次元目がlongitudeだとすると, dim_0_name = sst!0 ; とするとdim_0_nameには "latitude" が入る dim_1_name = sst!1 ; とするとdim_1_nameには "longitude" が入る のように,! で座標の名前を得ることができる。もし,変数olr(:,:)にsstと同じ座標名をつけたければ, olr!0 = dim_0_name olr!1 = dim_1_name または, olr!0 = sst!0 olr!1 = sst!1 とすればよい。
lat = sst&latitude lon = sst&longitude のようにして得られる。例えば,sstのlatitudeが(南極から北極まで1度刻みだとして)-90から90まで1ずつなら,latにはそのような配列が入る。なお,次元の名前が入った文字列を $ $ で囲み, lat = sst&$sst!0$ lon = sst&$sst!1$ のような表記も可能。 VAR = (/"latitude","longitude"/) lat = sst&$VAR(0)$ lon = sst&$VAR(1)$ これでも同じ結果となる。ある変数の座標を別の変数にも付加する場合にはcopy_VarCoordsなどの関数も使える。
a = slp(lat|:,lon|:) ; もともとこの形の全球の緯度・経度の格子情報を持った配列だとする a = slp(lon|:,lat|:) ; 次元の順番が入れ替わる a = slp({0:90},:) ; 北半球だけ取り出される なお,複数の次元のうち一部だけ次元名で指定することはできないので,次元名を用いて扱う時は全ての次元を指定すること。 VAR = (/"time","lat","lon"/) a = tmp($VAR(1)$|:,$VAR(2)$|:,$VAR(0)$|:) $ $ を駆使して汎用性の高いスクリプトを書けるようになるとカッコいい。 Attributes †変数には。例えば,気圧のデータを読み込むと,気圧の値が入った変数pressureに単位が"units"というAttributeとして付加されていたりする。Attributeは@を用いて指定する。 pressure@units = "Pa" のようにする。 a@_FillValue = -999. とする。未定義値を正しく設定しておかないと,関数が期待通りの値を返してくれないので注意。 a = (/1., 2., -999., 3./) ; a@_FillValueが設定されていないとする。 b = avg(a) ; avgは平均を求める関数。この場合,bは-248.25となる。 a@_FillValue = -999. ; -999.は未定義値。avgでは無視される。 b = avg(a) ; この場合,bは2.となる。
条件分岐 †IF文 if (条件) then Trueのときの操作 end if if (条件) then Trueのときの操作 else Falseのときの操作 end if else if を並べる場合,if の数だけend ifが必要。 if (条件1) then 1がTrueのときの操作 else if (条件2) then 1がFalse,2がTrueのときの操作 else if (条件3) then 1がFalse,2がFalse,3がTrueのときの操作 else 1がFalse,2がFalse,3がFalseのときの操作 end if end if end if バージョン6.4.0から待望のelseifが導入された(スペースなし)。 if (条件1) then 1がTrueのときの操作 elseif (条件2) then 1がFalse,2がTrueのときの操作 elseif (条件3) then 1がFalse,2がFalse,3がTrueのときの操作 else 1がFalse,2がFalse,3がFalseのときの操作 end if ループ †DO文 do i = 0, n ,2 ループさせたい操作 end do DO WHILE文 do while (条件) ループさせたい操作 end do continueで次のステップへスキップ,breakでループを抜ける。 (注意) NCLはコンパイルを必要としないスクリプト言語ゆえに、DOループを使いすぎると極端に遅くなることに留意。 文字列 †文字列は " " で囲んであらわす。
"m/s~S~2" "~S~O~N~C" ; Cの前に上付きのOを付けている "H~B~2~N~O"
"~F8~Dd" ; Δδ と書く。すなわち,~F番号~で呼び出し,それ以降が設定したFont tableになる。
"n~H-13V2F35~D~FV-2H3~" ; ñ (スペイン語のeñe) とすると,まずnが書かれ,~H-13V2F35~Dにより,35番フォントのDが左やや上にずれて書かれる。実はF35には文字装飾記号が含まれていて,Dには~(ティルダ)が割り当てられている。これによりnの上に~が重なってñとなる。そのあとの~FV-2H3~は他の文字を続ける場合にはフォントと位置を元に戻すために必要。 "(u~H-20V15F18~o~FV-15H-3B~L~NF34~ W Q~F~h~B~B~N~)~B~B" 書式指定子 †sprintfなどの関数において,数値から文字列へ変換する際の書式指定子。書式指定子は %[-][+][最小出力幅].[精度][変換のタイプ] のように書く。 -は,最小出力幅が出力内容よりも長い場合に左寄せを行うことを示す。 +は,非負の数に「+」を付加することを示す。 精度は,小数(f)および指数(e, E)の場合,小数点以下の桁数を示す(デフォルトは6)。g, Gの場合,整数部と小数部を合わせた桁数を示す。整数(i)の場合には,ここで指定した桁数が出力内容を超えるとき,その分だけ左に0が補填される。 変換のタイプは,fが小数,eまたはEが指数を示す。gまたはGのとき,精度の桁数に収まらない場合と絶対値が10^-4よりも小さい場合に指数表記,それ以外の場合に小数表記が用いられる。
x = 25.8136 y = -0.0000147 z = 1517 print( sprintf("%5.2f", x) ) print( sprintf("%+5.2f",x) ) print( sprintf("%8.2f", x) ) print( sprintf("%-8.2f",x) ) print( sprintf("%9.2e", x) ) print( sprintf("%9.2e", y) ) print( sprintf("%9.4g", x) ) print( sprintf("%9.7g", x) ) print( sprintf("%9.4g", y) ) print( sprinti("%7.0i", z) ) print( sprinti("%0.7i", z) ) print( sprinti("%7.5i", z) ) この結果は, 25.81 +25.81 25.81 25.81 2.58e+01 -1.47e-05 25.81 25.8136 -1.47e-05 1517 0001517 01517 関数の定義 †x1, x2を引数とするfunction_nameという関数をつくりたいとすると, undef("function_name") function function_name (x1, x2) ; function_nameを定義,引数を指定 local Y1, Y2, Y ; 関数のなかみだけで使う変数を書く begin 関数のなかみ(いろいろ計算して,欲しい変数Yを得る) return(Y) ; 戻り値 end returnがなければprocedureとして使える。 undef("procedure_name") procedure procedure_name (x1, x2) ; function_nameを定義,引数を指定 local Y1, Y2 ; 関数のなかみだけで使う変数を書く begin procedureのなかみ(いろいろ計算) end
NCL-mode †https://www.ncl.ucar.edu/Applications/editor.shtml を参照。 これらを入れるとNCLの関数やResourcesに色がつくようになる。 おすすめはemacsの場合,el-getというLispを入れることである。基本的には https://github.com/dimitri/el-get#installation の The Lazy Installer に紹介されているコマンドでインストールして http://blog.ogatomo.com/blog/2014/01/08/migration-to-el-get/ で紹介されているload-pathを.emacsなりinit.elに書くだけである。 el-getをインストールすれば,あとはM-x el-get installでncl-modeとauto-completeを導入すれば良い。すると,NCLのコマンドが自動で補完されるようになる。デザインを変更したり自作の関数にも対応したければ,ncl-mode.elまわりのファイルを書き換えてコンパイルし直せばよい。 ちなみに,私の普段のNCLスクリプト編集画面は例えばこんな感じ。下ではmpGeophysicalまで入力したところで,候補を表示してくれている。 |