NCLスクリプト - NCL tips

NCLスクリプトの書き方に関する説明をまとめました。


基本構成

とりあえずNCLスクリプトの中身を見てみよう。

以下のスクリプトはhttp://www.ncl.ucar.edu/Applications/xy.shtmlから引用したものである。 ところどころ省略、着色している。最初なので変数はイタリックにした。

  1. ;***********************************************
  2. ; xy_1.ncl
  3. ;
  4. ; Concepts illustrated:
  5. ; - Drawing a black-and-white XY plot
  6. ;
  7. ;************************************************
  8. load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/gsn_code.ncl"
  9. load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/gsn_csm.ncl"
  10. ;************************************************
  11. begin
  12. ;************************************************
  13. ; read in data
  14. ;************************************************
  15. f = addfile("$NCARG_ROOT/lib/ncarg/data/cdf/uv300.nc","r")
  16. u = f->U ;get u data
  17. ;************************************************
  18. ; plotting parameters
  19. ;************************************************
  20. wks = gsn_open_wks("ps","xy") ; open + workstation
  21. res = True ; plot mods desired
  22. res@tiMainString = "Basic XY plot" ; add title
  23. plot = gsn_csm_xy(wks,u&lat,u(0,:,{82}),res) ; create plot
  24. end
  • まず,8~9行目においてloadという関数で.nclファイルを読み出している。この中にはNCLで解析・描画するために必要な関数が入っている。そのため,おまじないのように呼び出しておく。上で呼んでいる2つ以外には contributed.ncl や shea_util.ncl を読み込めばだいたいのことはできるだろう。
    他に必要になったら https://www.ncl.ucar.edu/Document/Functions/list_type.shtml を見て必要なスクリプトをloadすれば良い。もちろん自分で関数を定義した.nclファイルを作り,呼び出すこともできる。
    なお,環境変数NCL_DEF_SCRIPTS_DIRを設定すると,そのディレクトリ内にある全ての.nclスクリプトをディレクトリエントリ順にロードする。上の例の場合, NCL_DEF_SCRIPTS_DIR=$NCARG_ROOT/lib/ncarg/nclscripts/csm とすればloadの行を省略することができる。また,バージョンによってはいくつかのスクリプトは自動で読まれるようになっている(6.5.0以降ほぼ全てのスクリプトがあらかじめ読み込まれる)。
  • 11行目~24行目のbeginからendの間がスクリプトの本体。これもおまじないのように書いておく。
  • 15行目にaddfileという関数が出てきている。この関数は読み書きするファイルを選択する関数である。"r"はファイルを読み込むことを意味する。(データの読み書き参照)
  • 16行目でaddfileで開いたNetCDFファイルからUという変数を取り出し、uというNCL上の変数(配列)に代入している。
  • 20行目のwksという変数名はwork stationの略で、出力する方式やサイズ、場所を決めているものである。gsn_open_wksという関数でwksを設定する。ここではPSファイルで出力し、名前は“xy.ps”とすることを指示している。
  • 21行目のresという変数はResourcesの略で,図の描き方を指示している。22行目でres@以下のtiMainStringを設定しタイトルを描いている(消すとタイトルが消えるはず)。他にもコンターの調整や、縦軸横軸の最大値、目盛りの幅、カラーバーの調整などなど、図に関する細かい設定はこれで行う。かなり細かく設定できるが、デフォルトでもだいたい綺麗に出る。NCLのお絵描きはresの設定にかかっている気がする。(描画に関する設定参照)
  • 23行目の関数gsn_csm_xyで図を出力している。だいたい gsn~ で出力することが多い。


@とか->とか変な記号が出てきているが、とりあえずまとめると

まず load → データを読み込む → データの処理・計算
 → wks の設定(出力方法) → res の設定(描画設定) → gsn~(プロット)

てな感じ。

reswksの変数名は,予約語でなければ何でもよい。


実行

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)ことができる。


コマンドラインを通して変数に値を与えることもできる。例えば,スクリプト内で変数year, titleが定義されずに用いられている場合,そのまま実行するとエラーとなるが,

$ ncl year=2015 'title="NCLtips"' hoge.ncl

のように代入してあげれば動く(isdefinedを使ってコマンドライン上で与えられなかった時の値を設定することもできる)。


バージョン6.5.0以降では,多くのスクリプトをpreloadするようになった。そのため,短いnclスクリプトを大量に走らせる場合においては,実行が遅くなる影響が無視できない可能性がある。そのときにはオプション -s によりpreloadせずに実行することができる。


記法

NCLスクリプトの書き方いろいろ。スクリプトの書き方に関してはスクリプト例一覧も参照のこと。


コメントアウト

各行において;以降はコメントアウトされる。

; この行はすべてコメント
a = 1  ; a = 1は実行されるが,この文はコメント

また,バージョン6.4.0以降はブロックコメントアウトが導入され,/;から;/までがコメントアウトされる。

/;
  ここはコメント
  ここもコメント
  ここもコメント
;/


変数の型

  • 数値型
カテゴリサイズ最小値最大値デフォルト未定義値(_FillValue)接尾辞
doublenumeric64 bits+/- 2.22507e-308+/- 8.98846e+3079.969209968386869e+36d, D
int64enumeric64 bits-92233720368547758089223372036854775807-9223372036854775806q
uint64enumeric64 bits01844674407370955161518446744073709551614Q
long (64bit)numeric64 bits-92233720368547758089223372036854775807-2147483647l
ulong (64bit)enumeric64 bits0184467440737095516154294967295L
long (32bit)numeric32 bits-21474836482147483647-2147483647l
ulong (32bit)enumeric32 bits042949672954294967295L
floatnumeric32 bits+/- 1.175494e-38+/- 1.701411e+389.96921e+36(none)
integernumeric32 bits-21474836482147483647-2147483647i (optional)
uintenumeric32 bits042949672954294967295I
shortnumeric16 bits-3276832767-32767h
ushortenumeric16 bits06553565535H
bytenumeric8 bits-128127-127b
ubyteenumeric8 bits0255255B

数値型には,オリジナルの数値型であるnumericと5.2.0で追加されたenumeric(extra-numeric)のカテゴリがある。 long, ulong型はシステムのbit数でサイズ等が異なる。


  • 非数値型
サイズデフォルト未定義値(_FillValue)
logicalN/AMissing
stringN/A"missing"
character8 bits0x00
graphicN/A-1
fileN/A-1
groupN/A-1
listN/A-1

logicalは論理型で,True,False,Missingのいずれかである。ただし,Missingを論理型変数あるいはAttributeに直接代入するためには,_Missingとする必要がある。

stringは文字列型,characterは文字型。

graphicは,gsn_csm_contour_map_ceなどで出力されるオブジェクトへの参照を示す変数型。

fileはサポートされているファイル形式のファイルへの参照でaddfileなどで出力される。また,groupはHDFファイル内のグループへの参照を示す型である。

listは任意の型の変数を含むリストを示す変数型でありaddfilesなどの出力がこれにあたる。


演算記号

記号意味
+足し算
-引き算
*掛け算
/割り算
^累乗
%余り(modulus)
#行列積
<小さいほうを選択
>大きいほうを選択
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" 文字列の足し算


論理記号

記号意味
.eq.等しい
.ne.等しくない
.le.等しいか小さい
.ge.等しいか大きい
.lt.より小さい
.gt.より大きい
.and.かつ
.or.または
.not.否定
.xor.排他的論理和(exclusive)


改行

改行は\

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 /)

のように書く。


配列の添え字は,m番目からn番目までiずつなら,a(m:n:i)のように示す。Fortranとは違って,添え字は0からはじまる。つまり,

 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/)

である。変数の次元に座標が入っている場合,後述のように座標の値で指定することも可能である。


リスト(list)型の変数の場合,

c = [/a, b/]

のように記述する。変数の型や次元や大きさが異なっていても一つにまとめられる。
添え字は配列と同様に,c[:]やc[0]のように書く。リストは複数の(特にサイズの異なる)変数に対して同じ操作を行うときに有用である。複数の変数をそれぞれのメモリ領域を参照するポインタとして取り扱うものだと思えばいい。


次元と座標

配列の各次元に名前と座標を付けることができる。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"

のようにする。
変数の未定義値の情報は"_FillValue"として付加され,例えば変数aの未定義値を-999.にしたければ,

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.となる。


なおAttributeも $ $ を用いた表現が可能。ある変数のAttributeを別の変数にも付加する場合にはcopy_VarAttsという関数が使える。Attributeや座標情報など全てのメタデータを別の変数にコピーするときにはcopy_VarMetaを用いる。


条件分岐

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ループを使いすぎると極端に遅くなることに留意。


文字列

文字列は " " で囲んであらわす。


上付き文字・下付き文字を使いたいときには,"~S~"・"~B~"の後ろがそれぞれ上付き・下付きとなり,"~N~"で元に戻ることを用いる。例えば,"m/s2", "℃", "H2O" は次のように書く。

"m/s~S~2"
"~S~O~N~C"    ; Cの前に上付きのOを付けている
"H~B~2~N~O"


改行したいときは,"~C~"を用いる。


ギリシャ文字を使いたい場合,NCLにはFont tableが用意されていてそれを呼び出して使う。例えばギリシャ文字である8番のFont tableを使うと,

"~F8~Dd"  ; Δδ

と書く。すなわち,~F番号~で呼び出し,それ以降が設定したFont tableになる。
F8やF33がギリシャ文字で,F18やF34に数学記号がある。他は
https://www.ncl.ucar.edu/Document/Graphics/font_tables.shtml
を参照の事。F136などで天気図記号も出せる。


また,"~H~"や"~V~"を用いると文字の位置を微調整することができる。Hのあとに正の値を入れると右に,負の値を入れると左にずれる。Vのあとに正の値を入れると上に,負の値を入れると下にずれる。たとえば,

"n~H-13V2F35~D~FV-2H3~"  ; ñ (スペイン語のeñe)

とすると,まずnが書かれ,~H-13V2F35~Dにより,35番フォントのDが左やや上にずれて書かれる。実はF35には文字装飾記号が含まれていて,Dには~(ティルダ)が割り当てられている。これによりnの上に~が重なってñとなる。そのあとの~FV-2H3~は他の文字を続ける場合にはフォントと位置を元に戻すために必要。
別の例として,次の式 FillArrow はこのように書ける。

"(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


自分で作った関数をまとめたスクリプトを置いておき,その中の関数を使いたいときは load で呼び出すことができる。


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まで入力したところで,候補を表示してくれている。

Emacs_NCLmode_sample


トップ   編集 凍結 添付 名前変更   新規