Link Search Menu Expand Document

pt_timvar程式說明

Table of contents

有關pt_timvar的程式背景、策略構思、後處理及運作流程等等,詳見CMAQ點源變數檔案之準備

版本說明

  • 版本:
    • TWN_3X3範圍、資料來源為TEDS資料庫,點源座標系統為LL
    • EAsia_81K範圍,除了TEDS之外,還讀取REAS處理結果

粗細網格pt_timvar程式版本之比較

項目EAsia_81KTWN_3X3說明
資料庫REAS+TEDSTEDS前者範圍較大需要REAS資料庫
引數及主要檔逐月CAMx高空點源nc檔案逐月CAMx高空點源nc檔案(same)
其他輸入檔point_reas16’+mm+’.csv’, reas2cmaq.json, template.timvar.nctemplate.timvar.nccsv與json詳見https://sinotec2.github.io/FAQ/2022/07/06/REASPtRd.html
結果檔名teds11.YYMM.timvar.ncteds11.YYMM.timvar.nc(same)
單月結果檔案容量4.1G3.9G 

執行方式

  • 目的:產生CMAQ所需的點源排放量中隨時間變化的部分,逐時數據
  • 執行:
    • 引數:逐月CAMx高空點源nc檔案(相對其他輸入檔,此檔為主要輸入檔)
for MM in {01..12};do 
python pt_constLL.py fortBE.413_teds11.ptse$MM.nc;
done
  • MM=01~12
  • 輸入檔(ptse檔案):ptsEnHRBE.py結果
  • 結果:teds11.YYMM.timvar.nc

TWN_3X3版本分段說明

模組及時間標籤轉換

  • 副程式(dt2jul及jul2dt)
     1  from pandas import *
     2  import numpy as np
     3  import netCDF4
     4  import PseudoNetCDF as pnc
     5  import datetime
     6  import os,sys,json
     7  import subprocess
     8
     9  def dt2jul(dt):
    10    yr=dt.year
    11    deltaT=dt-datetime.datetime(yr,1,1)
    12    deltaH=int((deltaT.total_seconds()-deltaT.days*24*3600)/3600.)
    13    return (yr*1000+deltaT.days+1,deltaH*10000)
    14  def jul2dt(jultm):
    15    jul,tm=jultm[:]
    16    yr=int(jul/1000)
    17    ih=int(tm/10000.)
    18    return datetime.datetime(yr,1,1)+datetime.timedelta(days=int(jul-yr*1000-1))+datetime.timedelta(hours=ih)
    19

讀入檔案名稱fname1

  • 由其中讀取月份,計算月份的起迄日期
  • 小時
    • CAMx月份起始自前月最末日之8時(LST)
    • CMAQ月份起始自0時(UTC),亦即二者時間一致,因此不另調整。
  • 日期
    • CAMx月份起始自前月最末日
    • CMAQ起始與結束日期則要看批次的定義,起始自前月15日後的16日,結束於第48日,共執行32天。
    20  fname1=sys.argv[1]
    21  mm=fname1[-5:-3]
    22  mon=int(mm)
    23  last=datetime.datetime(2019,mon,1)+datetime.timedelta(days=-1)
    24  begP=last#+datetime.timedelta(days=0.5) #pt files begin last day 12:00(UTC)
    25  begd=datetime.datetime(last.year,last.month,15)
    26  start=begd+datetime.timedelta(days=4*(5-1))
    27  idx=int((begP-start).seconds/3600)
    28  enddt=begd+datetime.timedelta(days=4*12)
    29  totalH=((enddt-start).days+1)*24+1

開啟CAMx點源處理結果

  • mpsp:部分CMAQ對照CAMx的名稱
    30  #input the CAMx ptsource file
    31  pt=netCDF4.Dataset(fname1, 'r')
    32  v2=list(filter(lambda x:pt.variables[x].ndim==2, [i for i in pt.variables]))
    33  nt,nopts=pt.variables[v2[0]].shape
    34  mpsp={'PNA':'NA','POC':'POA','XYLMN':'XYL'}
    35
    36

定義輸出檔案名稱

  • 使用ncks切割模版成為輸出檔並開啟之。
  • 記錄全域屬性變數
    37  #nc=/home/cmaqruns/2016_12SE1/emis/inln_point/ptnonipm/inln_mole_ptnonipm_20160701_12US1_cmaq_cb6_2016ff_16j.nc
    38  #ncks -d TSTEP,0,0 -d ROW,1,25000 $nc template.timvar.nc
    39  fname0='template.timvar.nc'
    40  fname=fname1+'.timvar.nc'
    41  path={'114-32-164-198.HINET-IP.hinet.net':'/opt/anaconda3/bin/', 'node03':'/usr/bin/','master':'/cluster/netcdf/bin/'}
    42  path.update({'DEVP':'/usr/bin/','node01':'/cluster/netcdf/bin/','node02':'/cluster/netcdf/bin/'})
    43  hname=subprocess.check_output('echo $HOSTNAME',shell=True).decode('utf8').strip('\n')
    44  if hname not in path:
    45    sys.exit('wrong HOSTNAME')
    46  if nopts>25000:sys.exit('nopts>25000')
    47  os.system(path[hname]+'ncks -O -d ROW,1,'+str(nopts)+' '+fname0+' '+fname)
    48  XCELL=pt.XCELL
    49  XORIG=pt.XORIG
    50  YORIG=pt.XORIG
    51  V=[list(filter(lambda x:pt.variables[x].ndim==j, [i for i in pt.variables])) for j in [1,2,3,4]]
    52

開啟輸出檔

  • 給定全域屬性變數
    • 原點及點源座標會因網格系統的定義而改變。
      53  nc = netCDF4.Dataset(fname,'r+')
      54  jt=nt
      55  v4=list(filter(lambda x:nc.variables[x].ndim==4, [i for i in nc.variables]))
      56  bdate=jul2dt([pt.SDATE,pt.STIME])
      57  nc.NROWS=nopts
      58  nc.GDNAM='TWN_3X3'
      59  nc.P_ALP = np.array(10.)
      60  nc.P_BET = np.array(40.)
      61  nc.P_GAM = np.array(120.98999786377)
      62  nc.XCENT = np.array(120.98999786377)
      63  nc.YCENT = np.array(23.6100196838379)
      64  nc.XCELL=XCELL #27000.000
      65  nc.YCELL=XCELL #27000.000
      66  nc.XORIG=XORIG #-877500.0
      67  nc.YORIG=XORIG #-877500.0
      68  nc.SDATE=dt2jul(start)[0]
      69  nc.STIME=dt2jul(start)[1]
      70
      

      給定全月的時間標籤,藉此擴張檔案長度。

  • 將所有變數矩陣全數歸0。
    71  for t in range(totalH):
    72    dt=start+datetime.timedelta(hours=t)
    73    nc.variables['TFLAG'][t,:,0]=[dt2jul(dt)[0] for j in range(nc.NVARS)]
    74    nc.variables['TFLAG'][t,:,1]=[dt2jul(dt)[1] for j in range(nc.NVARS)]
    75
    76  z=np.zeros(shape=nopts,dtype='float32')
    77  for v in v4:
    78    for t in range(totalH):
    79      nc.variables[v][t,:,:,:]=z

給定CMAQ之PMOTHR排放量

  • 給定其他特殊的粒狀物、VOC排放量(變數名稱對應不到的部分)
  • 轉換單位由小時到秒
      80  #CAMx_pt part
      81  pmother=(pt.variables['CPRM'][:,:]+pt.variables['FPRM'][:,:])/ 3600.
      82  nc.variables['PMOTHR'][idx:idx+jt,0,:nopts,0]=pmother
      83  for v in mpsp: #mpsp={'PNA':'NA','POC':'POA','XYLMN':'XYL'}
      84    if mpsp[v] not in V[1]:continue
      85    nc.variables[v][idx:idx+jt,0,:nopts,0]=np.array(pt.variables[mpsp[v]][:,:nopts], dtype='float32')/ 3600.
    

    將CAMx檔案內容倒入新檔

      86  for v in v2: #ptse from CAMx
      87    if v not in v4:continue #outside of v4 (the template)
      88    print(v)
      89    var=pt.variables[v][:,:]/3600. #gmole/hr -> gmole/sec
      90    nc.variables[v][idx:idx+jt,0,:nopts,0]=var[:]
    

    處理NOX的部分

    -如果CMAQ要求的時間範圍較CAMx大,則以最靠近的日變化代入。

    91  nox= nc.variables['NO2'][:,0,:,0]+nc.variables['NO'][:,0,:,0]
    92
    93  nc.variables['NO2'][:,0,:,0]=nox *1./10.
    94  nc.variables['NO'][:,0,:,0] =nox *9./10.
    95  #np.zeros(shape=(jt,nopts),dtype='float32') #
    96
    97  for v in v4:
    98    if np.sum(nc.variables[v][:])==0:continue
    99    if idx>0:
   100      for t in range(idx):
   101        nc.variables[v][t,0,:,0]=nc.variables[v][t+24,0,:,0]
   102    if totalH>idx+jt:
   103      for t in range(idx+jt,totalH):
   104        nc.variables[v][t,0,:,0]=nc.variables[v][t-24,0,:,0]
   105  # print(v)
   106  nc.close()

EAsia_81K 版本差異

讀取point_reas16MM.csv長度

$ diff ./REAS/pt_timvar.py ./twn/pt_timvarLL.py
...
28,30c29
< totalH=(enddt-start).days*24+1
< #input the results of pt_const.py fortBE.14.teds.baseMM
< len_df=int(subprocess.check_output('wc -l '+'point_reas16'+mm+".csv |awk '{print $1'}",shell=True).decode('utf8').strip('\n'))-1
---
> totalH=((enddt-start).days+1)*24+1

總點源個數與座標系統

50,53c46,52
< #if nopts+len(df)>10000:sys.exit('nopts+len(df)>10000')
< os.system(path[hname]+'ncks -O -d ROW,1,'+str(nopts+len_df)+' '+fname0+' '+fname)
< XCELL=81000.000
< XORIG=-2389500.
---
> if nopts>25000:sys.exit('nopts>25000')
> os.system(path[hname]+'ncks -O -d ROW,1,'+str(nopts)+' '+fname0+' '+fname)
> XCELL=pt.XCELL
> XORIG=pt.XORIG
> YORIG=pt.XORIG
> V=[list(filter(lambda x:pt.variables[x].ndim==j, [i for i in pt.variables])) for j in [1,2,3,4]]
57,58c56,58
< nc.NROWS=nopts+len_df
< nc.GDNAM='EAsia_81K'
---
> bdate=jul2dt([pt.SDATE,pt.STIME])
> nc.NROWS=nopts
> nc.GDNAM='TWN_3X3'

展開模版檔案

  • 在nc檔案內使用迴圈非常費時,應儘量避免。
    • 54~79是展開模版的歷程,無法避免。但是讓時間標籤先填入0,展開後再整批帶入,會比一一對照要快很多。
    • 77~78開啟mask的過程,是最花時間的段落,因為nc基本上是個DICT、不適用整批帶入,沒辦法再更快。
    • 關閉檔案再開啟:經測試並不會減省大量的記憶體。反而過多IO拖慢程式的速度
    • 模版之展開(line 71~72)
    • TFLAG填入日期時間(73~76)
      • 使用dt2jul函數,將datetime.timedelta計算結果轉成日期及時間陣列
      • 整理成nt2的陣列,轉到ntNVARS*2的大陣列
      • 一併填入TFLAG內
      • 如果有ETFLAG可以參考mod_tflag.py(適用uamiv檔案,如果沒有ETFLAG、aok等等很多程式會出錯)
    • 防止陣列被mask鎖住(77~78)
    • 儘量不要用2個nc檔案直接傳遞數值。
    54    nc = netCDF4.Dataset(fname,'r+')
...
    71    for t in range(totalH):
    72      nc.variables['TFLAG'][t,:,:]=0
    73    sdt=np.array([dt2jul(start+datetime.timedelta(days=t/24.)) for t in range(totalH)]).flatten().reshape(totalH,2)
    74    ARR=np.zeros(shape=(totalH,nc.NVARS,2))
    75    ARR[:,:,:]=sdt[:,None,:]
    76    nc.variables['TFLAG'][:,:,:]=ARR[:,:,:]
    77    for v in v4:
    78      nc.variables[v][:,0,:,0]=0. 
    79    print('save blank file')
  • mod_tflag.py之應用
    • 本程式是為製作2019年各月CAMx模擬結果的模版,藉此執行aok以匯總EPA測站數據之ovm.csv/ovm.dat_camx,藉以快速繪製時序圖。
    • d_hrs是TFLAG/ETFLA的差異,前者是模擬開始時間、後者是TFLAG+TSTEP,模擬結束時間
      • 此處TSTEP是1小時
    • 應用前述批次倒入TFLAG之作法

REAS 排放對照及轉檔

  • 讀取csv及json檔案
  • 先跳過NOx隨後再處理
  • 重複使用ARR:並使用空維度None,對速度提升與降低記憶體使用都很有效
  • 執行完畢將變數歸0,對減少記憶體也不無幫助(98~99)
    81    #CAMx_pt part
    82    mpsp={'PNA':'NA','POC':'POA','XYLMN':'XYL'}
    83    ARR=(pt.variables['CPRM'][:,:]+pt.variables['FPRM'][:,:])/ 3600.
    84    nc.variables['PMOTHR'][idx:idx+jt,0,:nopts,0]=ARR[:,:]
    85    with_data=['PMOTHR']
    86    
    87    for v in mpsp: #mpsp={'PNA':'NA','POC':'POA','XYLMN':'XYL'}
    88      if v not in mpsp or mpsp[v] not in v2:continue
    89      ARR[:,:]=np.array(pt.variables[mpsp[v]][:,:nopts], dtype='float32')/ 3600.
    90      nc.variables[v][idx:idx+jt,0,:nopts,0]=ARR[:,:]
    91      with_data.append(v)
    92    
    93    for v in v2: #ptse from CAMx
    94      if v not in v4:continue #outside of v4 (the template)
    95      ARR[:,:]=np.array(pt.variables[v][:,:nopts],dtype='float32') / 3600. #gmole/hr -> gmole/sec
    96      nc.variables[v][idx:idx+jt,0,:nopts,0]=ARR[:,:]
    97      with_data.append(v)
    98    pt=0.
    99    ARR=0.
  • 這部分容量並不增加、計算速度也不低,
   100    #REAS part
   101    print('REAS part')
   102    df=read_csv('point_reas16'+mm+'.csv')
   103    specs=[ i for i in df.columns if i not in ['lon','lat','x_m','y_m']]
   104    ARR=np.zeros(shape=(jt,len_df))
   105    with open('reas2cmaq.json','r') as f:
   106      r2c=json.load(f)
   107    for v in specs:
   108      if v == 'NOX':continue
   109      if sum(np.array(df[v]))==0.:continue
   110      if v not in r2c :continue
   111      if type(r2c[v])==str: #simple mapping of specs
   112        if r2c[v] not in v4:continue
   113        a=np.array(df[v])
   114        ARR[:,:]=a[None,:]
   115        nc.variables[r2c[v]][idx:idx+jt,0,nopts:,0]=ARR[:,:]
   116        with_data.append(r2c[v])
  • CBM成分累加部分(117~133)與nc檔案脫鉤,在時間迴圈之外執行,這樣才有效率
   117    cbm=set()
   118    sv=[]
   119    for v in specs:
   120      if v not in r2c :continue
   121      if type(r2c[v])!=str:
   122        sv.append(v)
   123        cbm=cbm.union(set(r2c[v]))
   124    cbm=list(cbm)
   125    ARR=np.zeros(shape=(len(cbm),len_df))
   126    for v in sv:
   127      for i in r2c[v]:
   128        ARR[cbm.index(i),:]+=np.array(df[v])
   129    Acbm=np.zeros(shape=(jt,len(cbm),len_df))
   130    Acbm[:,:,:]=ARR[None,:,:]
   131    for v in cbm:
   132      nc.variables[v][idx:idx+jt,0,nopts:,0]=Acbm[:,cbm.index(v),:]
   133      with_data.append(v)
  • REAS氮氧化物排放量之讀取
144,147d92
< ARR=np.zeros(shape=(jt,len_df))
< a=np.array(df['NOX'])
< ARR[:,:]=a[None,:]
< nox[idx:idx+jt,nopts:]=ARR[:,:]

前一天排放補值

CAMx 是逐月檔,但在CMAQ是跟著WRF的批次,因此可能在每月的前後會需要多一點時間(小月問題),過去以手動方式加值,現則直接以程式添加。

   135    for v in v4:
   136      if v not in with_data:continue
   137      a=nc.variables[v][24:idx+24,0,:,0]
   138      nc.variables[v][:idx,0,:,0]=a[:,:]
   139      a=nc.variables[v][idx-jt-24:totalH-24,0,:,0]
   140      nc.variables[v][idx-jt:totalH,0,:,0]=a[:,:]
   141    
   142    nox= nc.variables['NO2'][:,0,:,0]+nc.variables['NO'][:,0,:,0]
   143    
   144    ARR=np.zeros(shape=(jt,len_df))
   145    a=np.array(df['NOX'])
   146    ARR[:,:]=a[None,:]
   147    nox[idx:idx+jt,nopts:]=ARR[:,:]
   148    nc.variables['NO2'][:,0,:,0]=nox *1./10.
   149    nc.variables['NO'][:,0,:,0] =nox *9./10.
   150    # print(v)
   151    nc.close()

程式下載

Reference

  • Here pt_timvarLL.py程式說明
  • Code pt_timvarLL.py轉換點源檔之逐時部分