2015年3月16日

Day 6 增加效率:善用批次功能

編寫 script 檔如果不能善用批次處理 (batch processing),那就真是太浪費自己的大好青春時光了。不光是NCL,這個道理適用於所有的程式編寫。

對於寫程式這件事情不熟悉的人,這邊舉一個例子:假設今天的任務是「讀取一份一整年的氣溫資料,要畫每天一張的全球氣溫分布圖,圖上面的標題要寫上當天的日期。」圖的標題一直在變,如果不能善用批次功能自動設定每張圖的標題,一年365張圖,就要手動更改script檔並手動執行365次...。利用批次處理,可以只執行一次,完成相同的任務。

對於作圖目的取向,學習基本的批次功能,不外乎找到以下幾個問題的答案:
如何使用 if 判斷式?
如何使用迴圈 (loop)?
如何擷取、組合字串?
如何抓取變數資訊 (e.g., 名稱、維度、單位...)?
對於Linux中的NCL,多了一個問題:
如何在 NCL script 中與 Linux 互動?

下面用 NCL101-8.ncl 作為範例,示範一些常用到的工具與函數,同樣的就直接在程式碼中做說明以精簡版面。day6_example.pdf 可在這下載。

;===== NCL101-8.ncl =====
load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/gsn_code.ncl"
load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/gsn_csm.ncl"

begin

OutputPDFName = "day6_example"
print("Delete old " + OutputPDFName + ".pdf at the beginning.")
; OutputPDFName 是一個字串變數。組合在一起後這一行就同等於
; print("Delete old day6_example.pdf at the beginning.")
; 在NCL中可以利用 + 號自由的將「字串變數」和「一般字串」組合在一起。
; 而更方便的是,NCL對於字串的限制非常寬鬆 (聰明?),
; 整數/浮點數也可以直接拼進字串內,無須先把數字轉換成字串格式
; (可能要學過別的程式(Matlab/Fortran)才會知道上一行在說什麼...)
system("rm -f " + OutputPDFName + ".pdf")
CurrentPDFList = systemfunc("ls -l *.pdf")
; 在script中也可以直接下達Linux指令,活用這部分將對於批次處理有莫大的幫助。
; system 和 systemfunc 裡面都可以直接下達 Linux 指令,
; 差別在於 systemfunc 會回傳執行結果回 NCL script,而 system 不會。
print("All .pdf in this folder...")
print("" + CurrentPDFList)
print("")

        wks_type                = "pdf"
        wks_type@wkPaperWidthF  = 8.0
        wks_type@wkPaperHeightF = 4.5
        wks                     = gsn_open_wks(wks_type,OutputPDFName)

        ncFile                  = addfile("air.2014.nc","r")
        Temp                    = ncFile->air(0:3,:,:,:)
        nDim                    = dimsizes(Temp)
; dimsizes 可以回傳變數的維度資訊
        nT                      = nDim(0)
        nZ                      = nDim(1)
        nY                      = nDim(2)
        nX                      = nDim(3)

        res                     = True
        res@gsnDraw             = False
        res@gsnFrame            = False
        res@mpCenterLonF        = 180.
        res@mpMinLatF           = 0.
        res@mpMaxLatF           = 80.
        res@mpMinLonF           = 90.
        res@mpMaxLonF           = 220.

        res_pn                  = True
        res_pn@gsnMaximize      = True
        res_pn@gsnPaperOrientation = "portrait"

        plot                    = new(6,graphic)

; 迴圈的寫法。起始,結束,間隔。
; 也可以遞減,就讓第一個數字大於第二個數字,間隔給負值。
do iT = 0,nT-1,1
do iZ = 0,nZ-1,3
; 判斷式 if 的用法也很直覺。
        if (iZ.eq.0) then
        iPlot = 0
        print("TimeStep = "+iT)
        end if

; 這是個「字串+整數=字串」的例子
        res@gsnLeftString       = "Level = " + iZ
        plot(iPlot)             = gsn_csm_contour_map_ce(wks,Temp(iT,iZ,:,:),res)
        iPlot = iPlot + 1
end do
; 別忘了變數的屬性 (@, attributes) 也是字串,很好用。
        res_pn@txString         = Temp@long_name + " TimeStep = " + iT
        gsn_panel(wks,plot,(/2,3/),res_pn)
end do

print("All Done.")
end
;=====

當然這幾招花拳繡腿打完不敢說自己的批次撰寫能力有多好,但的確是個不錯的開始了。一個個的小技巧實在太多太雜,無法一一列舉,但會隨著寫程式的年資慢慢的學習領悟、不斷地精進累積。

正所謂:師父領入門,修行在個人。





待續... Day 7

沒有留言:

張貼留言