VERDI圖面解析度之改善

Table of contents

前言

VERDI解析度不足的困難 

VERDI是模式模擬過程中常用、好用的顯示軟體,也是美國環保署提供給網格模擬社群的公開軟體。詳細說明可以參考VERDI官網或筆記。

一般來說VERDI的等值圖只有一種,就是方塊圖(Tile、或稱為raster、色階圖),並沒有等值線(contour)、等值色塊(shade)。為了忠實呈現輸入數據的數值與解析度,VERDI並不會進行空間與時間的內、外插,這對模擬過程中能確實檢討很有幫助。然而也有不方便的時候:

  • 做簡報、呈現結果的時候。就必須另外用別的軟體重做一遍。
  • 與其他不同來源數據進行比較的時候。彼此的解析度差異太大,視覺上無法做出直覺判斷。

解決方案

面對VERDI強烈的階梯狀的圖面,為了改善此一情況,使用者可以:

  • 提高模式的解析度。然而此舉增加電腦的負荷、輸出入資料的處理量、也涉及資料本身的解析度問題。
  • 提高色階數。此舉雖然可以有些遮掩,看起來似有連續的效果,但也模糊模擬結果的數量感。太多階層、太多資訊等於沒有資訊。
  • 將數據進行空間的內插。圖面色階仍然維持既定數量級,然而網格之間進行內插,以保持原數據的空間合理性。

潛在困難與最終方案

空間內插會遇到的問題:

  • 極值的消失:一般內插即為平緩均勻化(smothing),會將模式模擬的極大、極低值抹去。
  • 趨勢的消失:傳統的修邊程式是針對圖面上的鋸齒狀進行重新計算,是軟體層面的計算,並不考慮圖面的物理量,也可能造成趨勢的改變。 此處之解決方案:
  • 採水平空間的 三次仿樣函數,數值方法詳細可以參考網頁說明。可以避免極值或趨勢被改變
  • python的 三次仿樣函數範例,可以參考官網說明。

程式設計重點

讀取d01數據檔案 

  • 大多數模式輸出檔案都是5維的數據,然而也可能有4維的情形,如METCRO2D,沒有垂直項,因此需要做不同方式處理(line10~32)。
  • 為減省程式計算的時間,如果數據全為0(sum==0),不進行內插計算。(line13~17、23~27 )
  • 讀取後,計算格點的座標值(直角座標系統、以臺灣為中心)(line33~35)

模版之準備 

  • netCDF雖然方便,但因為屬性細節太多,最好還是由既有模版來改就好了。
  • 在此須事先準備好一IOAPI(model3 m3)格式、d01範圍、解析度為9Km的模版檔案(tmplateD1_9km.nc, line36~37 )。
  • hmp(home path為每個機器根目錄下第一層的定義,即為硬碟的目錄名稱,
    • master、DEVP、node01~node03為/nas1、
    • IMacKuang為/Users、
    • centos8為/data
    • (dev2為/nas3)
    • 每個機器設計的目錄名稱皆相同,可以減省系統平移時的困難。
  • 讀取後,計算新格點(nc1)的座標值(直角座標系統、以臺灣為中心)(line43~45),此一程式的重點即在將(x,y)的值,內插到(x1, y1)系統上。

新網格nc1 範圍數據之篩選

  • 確保選取到正確的點位進行內插,須先判斷點位是在新網格nc1的範圍內還是範圍外,先判斷x值、再進行y值的篩選。
  • xyc即為在nc1範圍內的(x, y)配對的序列(line 49~53),對應到各點的濃度,即可進行cubic spline內插。
  • 因新舊網格點位的對照(idxx, idxy, xyc…等)關係不會因為時間或污染物變數而有差異,因此必須先在迴圈外處理好,以減省時間。

時間與物理量項目之迴圈

  • 每一時間、物理量項目的趨勢、極值分布都不相同,必須逐一進行內插。(line 64~69)
  • 內插使用griddata來進行,方式採cubic,即為三次仿樣函數。
(py37)
kuang@master /nas1/ecmwf/near_real_time/gribs
$ cat -n D1_9km.py
     1  import netCDF4
     2  import numpy as np
     3  from scipy.interpolate import griddata
     4  
     5  import sys, os, subprocess
     6
     7  fname=sys.argv[1]
     8  nc = netCDF4.Dataset(fname,'r')
     9  V=[list(filter(lambda x:nc.variables[x].ndim==j, [i for i in nc.variables])) for j in [1,2,3,4]]
    10  if len(V[3])!=0:
    11    nt, nlay, nrow, ncol=nc.variables[V[3][0]].shape
    12    if nlay>1:sys.exit('not a vertical slice, please reduce LAY dim. by ncks "-d LAY,0,0" ')
    13    Vp=[]
    14    for v in V[3]:
    15      if np.sum(nc.variables[v][:])==0:continue
    16      Vp.append(v)
    17    nv=len(Vp)
    18    var=np.zeros(shape=(nv, nt, nrow, ncol))
    19    for iv in range(nv):
    20      var[iv,:,:,:]=nc.variables[Vp[iv]][:,0,:,:]
    21  elif len(V[2])!=0:
    22    nt, nrow, ncol=nc.variables[V[2][0]].shape
    23    Vp=[]
    24    for v in V[2]:
    25      if np.sum(nc.variables[v][:])==0:continue
    26      Vp.append(v)
    27    nv=len(Vp)
    28    var=np.zeros(shape=(nv, nt, nrow, ncol))
    29    for iv in range(nv):
    30      var[iv,:,:,:]=nc.variables[Vp[iv]][:,:,:]
    31  else:
    32    sys.exit('wrong matrix shapes')
    33  x_mesh=np.array([nc.XORIG+i*nc.XCELL for i in range(ncol)])
    34  y_mesh=np.array([nc.YORIG+j*nc.YCELL for j in range(nrow)])
    35  x, y = np.meshgrid(x_mesh, y_mesh)
    36  hmp=subprocess.check_output('pwd',shell=True).decode('utf8').strip('\n').split('/')[1]
    37  tempf='/'+hmp+'/ecmwf/near_real_time/tmplateD1_9km.nc'
    38  fnameO=fname+'9'
    39  os.system('cp '+tempf+' '+fnameO)
    40  nc1= netCDF4.Dataset(fnameO,'r+')
    41  V1=[list(filter(lambda x:nc1.variables[x].ndim==j, [i for i in nc1.variables])) for j in [1,2,3,4]]
    42  nt1,nlay1,nrow1,ncol1=nc1.variables[V1[3][0]].shape
    43  x1d=[nc1.XORIG+nc1.XCELL*i for i in range(ncol1)]
    44  y1d=[nc1.YORIG+nc1.YCELL*i for i in range(nrow1)]
    45  x1,y1=np.meshgrid(x1d, y1d)
    46  maxx,maxy=x1[-1,-1],y1[-1,-1]
    47  minx,miny=x1[0,0],y1[0,0]
    48  
    49  idxx = np.where(abs(x) <= (maxx - minx) /2+nc1.XCELL*10)
    50  xc,yc = x[idxx],  y[idxx]
    51  idxy = np.where(abs(yc) <= (maxy - miny) /2+nc1.YCELL*10)
    52  ny=len(idxy[0])
    53  xyc= [(x[idxx[0][idxy[0][i]],idxx[1][idxy[0][i]]],y[idxx[0][idxy[0][i]],idxx[1][idxy[0][i]]]) for i in range(ny)]
    54
    55  for v in Vp:
    56    zz=nc1.createVariable(v,"f4",('TSTEP','LAY','ROW','COL'))
    57    nc1.variables[v].long_name    = nc.variables[v].long_name
    58    nc1.variables[v].units        = nc.variables[v].units
    59    nc1.variables[v].var_desc     = nc.variables[v].var_desc
    60
    61  for t in range(nt):
    62    for iv in range(nv):
    63      nc1.variables['TFLAG'][t,iv,:]=nc.variables['TFLAG'][t,0,:]
    64  for t in range(nt):
    65    for v in range(nv):
    66      c = np.array([var[iv,t,idxx[0][idxy[0][i]], idxx[1][idxy[0][i]]] for i in range(ny)])
    67      if np.sum(c)==0: continue
    68      nc1.variables[Vp[iv]][t, 0, :, :] = griddata(xyc, c, (x1, y1), method='cubic')
    69  nc1.SDATE,nc1.STIME=nc1.variables['TFLAG'][0,0,:]
    70  nc1.TSTEP=nc.TSTEP
    71  nc1.NVARS=nv
    72  nc1.close()

CAMx模擬結果檢討

圖1為原始VERDI之圖面,由於數據解析度的限制(包括氣象與排放量檔案的解析度),網格為53X53,解析度為81km。在珠江三角洲的西南側有最高值,在嶺南地區有最低值。

D1_9Km1.png
原始VERDI之圖面,網格數53X53

圖2為內插後的結果(linear):階梯狀完全消除、海面上色階可以呈現出合理的曲線。最大值不變,但範圍略有減少,仍能保持濃度顯著的空間梯度。最低值不變,但範圍顯著減少。東南沿海的4個較高網格連成一個高濃度帶,有模糊化的趨勢。

D1_9Km2.png
線性內插結果,網格數增加9X9倍

圖3為內插後的結果(cubic):較線性內插更圓緩,容許更大的濃度梯度。高低值範圍較linear更寬廣。

D1_9Km3.png
cubic內插結果,網格數增加9X9倍

圖4為同一日ecmwf的GO3數據:模式對西側內地有低估,東南沿海則有高估。

D1_9Km4.png
GO3再分析數據之內插結果

參考資料與連結

  1. 連結
  2. cubic spline
  3. GO3
    • Michela Giusti(2019)https://confluence.ecmwf.int/display/CUSF/GEMS+ozone