Link Search Menu Expand Document

全球船隻排放量之處理_CAMx

Table of contents

背景

  • 船隻排放量不單在港口局部地區對空氣品質造成影響,也在公海等空曠領域造成背景空氣品質的差異,因此不論是哪一個解析度層面,船隻排放都非常的重要。
  • 有鑒於此,國際間對船隻排放多所研究。其中以芬蘭氣象研究所對全球的推估結果最具規模也最多引用,因此,本項作業乃以該資料庫為對象,進行解讀、轉檔、應用於空氣品質模擬。
  • 此處應用CAMx的前處理程式:
    • nc2m3、以及
    • 後處理程式slim、cbin等,
    • 最後應用python程式加進CMAQ面排放源裏。
  • mozart2camx程式中會將mozart的濃度單位(重量混合比)轉camx的濃度單位(ppm),公式 VMR = 28.9644 / mw * 1e6 * MMR 可以參考ecmwf網站。(詳下述)

Sources

  • Data
https://en.ilmatieteenlaitos.fi/surveying-maritime-emissions
http://www.globemission.eu/steam/data/NOx_allHeights_2015-01-01T00_2015-12-31T00.nc (not available)
  • 工作目錄位置
kuang@114-32-164-198 /Users/TEDS/MACMIP/RadioForce8.5/ships
kuang@master /nas1/TEDS/en.ilmatieteenlaitos.fi/surveying-maritime-emissions
  • Examples
https://safety4sea.com/new-study-reveals-shipping-pollution-hotspots-using-ais-data/
https://ars.els-cdn.com/content/image/1-s2.0-S1352231017305563-gr4_lrg.jpg
  • 實際上網站並未提供所有污染項目,僅有NOx,因此還需要用排放比例來轉換。

工作步驟

檔案切割準備

  • 由於檔案為等間距經緯度系統,可以用ncks進行裁切。
  • NOx_east_asia.nc History
/home/nco-4.6.9/src/nco/.libs/lt-ncks -O -d latitude,314,827 -d longitude,3013,3677 NOx_allHeights_2015-01-01T00_2015-12-31T00.nc 

NOx_east_asia.nc History

  • Wed Dec 25 17:21:14 2019: /home/nco-4.6.9/src/nco/.libs/lt-ncks -O -d latitude,314,827 -d longitude,3013,3677 - NOx_allHeights_2015-01-01T00_2015-12-31T00.nc NOx_east_asia.nc
  • odel version: STEAM 3.3.0 (JAVA, edited 31.1.2017)” ;
  •                 :references = “Jalkanen, J.-P. ,Johansson, L., et al. 2012. Extension of an assessment model of ship traffic - exhaust emissions for particulate matter and carbon monoxide” ;
  •                 :title = “modelled shipping exhaust emissions and statistics in WorldPool_CUSTOMaccording to STEAM model” ;
  •                 :info = “emission values describe emissions released at stack height between 0 and 100.” ;
  •                 :NCO = “4.6.9” ;

主程式

nox_all2.py 單位由kg/日/網格→g/hr/km^2 (CMAQ emis in second, CAMx emis in hour)

  • 由於MMR轉成MMV或粒狀物時,乘上不同轉換因子(氣狀物 VMR = 28.9644 / mw * 1e6 * MMR 單位為ppm、粒狀物ug/m3=MMR* 1e9 / (g/m3_air) ),
  • 在1013.25 hPa和15°C時,空氣的密度大約為1225 g/m³
from pandas import *
import datetime
import netCDF4
import numpy as np
import os,sys


pi=3.14159265359
peri_x=40075.02 #Km
peri_y=40008
r_x=peri_x/2./pi
r_y=peri_y/2./pi

#讀取NOx排放量
path=''
fname=path+'NOx_east_asia.nc'
nc = netCDF4.Dataset(fname,'r')
NOx=np.array(nc.variables['NOx'][:,:,:])
lst={'lat':list(nc.variables['latitude'][:])}
lst.update({'lon':list(nc.variables['longitude'][:])})
lst.update({'time':[i for i in range(31)]})

#讀取過去各物質加總結果,與NOx的比例,將乘上NOx ship排放量,來推估其他物質的排放量
df=read_csv('shp_sum.csv')
dfs=read_csv('shp_moz.csv')
dfs.iloc[0,0]='1,3,5-trimethylbenzene'
col=['spec','moz','mw']
dfs.columns=col
spec=list(dfs.spec);moz=list(dfs.moz);mw=list(dfs.mw)
spz={i:j for i,j in zip(spec,moz)}
mws={i:j for i,j in zip(spec,mw)}
nrow,ncol=(514,665)
dlon=(max(lst['lon'])-min(lst['lon']))/(ncol-1)
dlat=(max(lst['lat'])-min(lst['lat']))/(nrow-1)
prs=np.ones (shape=(31,nrow,ncol))*101806.9
tmK=np.ones (shape=(31,nrow,ncol))*298.

#計算(LL)各網格的面積
area=np.zeros(shape=(31,nrow,ncol))
for j in range(nrow):
  rad=abs(lst['lat'][j]/90.)*pi/2.
  r=(r_x*np.cos(rad)+r_y*np.sin(pi/2.-rad))/2.
  dx=2.*pi*r * dlon/360.
  dy=dlat/180.*(peri_x*np.cos(rad)**2+peri_y*np.sin(rad)**2)/2.
  for i in range(ncol):
    area[:,j,i]=[dx*dy for t in range(31)]
#print (np.max(area))
#sys.exit('area.txt')
#with open('area.txt','w') as f:
#  for j in range(nrow):
#    for i in range(ncol):
#      f.write(str(i)+' '+str(j)+' '+str(area[j,i])+'\n')

tmp=netCDF4.Dataset('frame.nc','r')
f48={'time':'f8', 'lat':'f4', 'lon':'f4'}
fi4={'P0':'f4', 'mdt':'i4', 'mhisf':'i4'}

#寫出每個月的nc檔案
for m in range(1,13):
  mm='{:02d}'.format(m)
  fname=path+'shp_east_asia'+mm+'.nc'
  #gregoria date
  lst.update({'time':[(datetime.datetime(2015,m,1)+datetime.timedelta(days=d)).toordinal() for d in range(31)]})

  if not os.path.isfile(fname):
    f = netCDF4.Dataset(fname,'w')
    f.createDimension('lat',nrow)
    f.createDimension('lon',ncol)
    f.createDimension('time',31)
    f.createDimension('lev',1)
    f.createDimension('ilev',2)
#   constants
    for s in ['P0', 'mdt', 'mhisf']:
      f.createVariable(s,fi4[s],dimensions=())
      f.variables[s][:]=tmp.variables[s][:]
      f.variables[s].long_name=tmp.variables[s].long_name
      if s=='mdt':f.variables[s][:]=[3600]

#   one dimension
    for s in ['time', 'lat', 'lon']:
      f.createVariable(s,f48[s],( s,))
      f.variables[s][:]=lst[s][:]
      f.variables[s].long_name=tmp.variables[s].long_name
      f.variables[s].units=tmp.variables[s].units
      if s=='time': f.variables[s].calendar=tmp.variables[s].calendar
    for s in ['ilev','lev']:
      f.createVariable(s,'f4',( s,))
      f.variables[s][:]       =tmp.variables[s][:]
      f.variables[s].long_name=tmp.variables[s].long_name
      f.variables[s].units    =tmp.variables[s].units
      f.variables[s].positive =tmp.variables[s].positive
      f.variables[s].A_var    =tmp.variables[s].A_var
      f.variables[s].B_var    =tmp.variables[s].B_var
      f.variables[s].P0_var   =tmp.variables[s].P0_var
      f.variables[s].PS_var   =tmp.variables[s].PS_var
      f.variables[s].bounds   =tmp.variables[s].bounds
    for s in ['date', 'datesec']:
      f.createVariable(s,'i',( 'time',))
      f.variables[s].long_name=tmp.variables[s].long_name
      if s=='date':
        f.variables[s][:]=[int((datetime.datetime(2015,m,1)+datetime.timedelta(days=d)).strftime('%Y%m%d')) for d in range(31)]
      else:
        f.variables[s][:]=[int(0) for d in range(31)]
        f.variables[s].units    =tmp.variables[s].units

#   three dimensions
    for s in ['PS']:
      f.createVariable(s,'f4',( 'time', 'lat', 'lon', ))
      f.variables[s][:,:,:]=prs[:,:,:]
      f.variables[s].units    =tmp.variables[s].units

#   four dimensions
    for s in set(moz):
      if s=='None':continue
      f.createVariable(s,'f4',( 'time', 'lev', 'lat', 'lon', ))
      f.variables[s].units    = "g/KM^2/hr"
    s='T'
    f.createVariable(s,'f4',( 'time', 'lev', 'lat', 'lon', ))
    f.variables[s].long_name = "T               "
    f.variables[s].units = "K               "
    f.variables[s].var_desc = "temperature"
    f.variables[s][:,0,:,:]=tmK[:,:,:]
  else:
    f = netCDF4.Dataset(fname,'r+')

  for s in set(moz):
    if s=='None':continue
    f.variables[s][:,0,:,:]=np.zeros(shape=(31,nrow,ncol))

  j0=(datetime.datetime(2015,m,1)-datetime.datetime(2015,1,1)).days
  dfm=df.loc[(df.mon==m)].reset_index(drop=True)
  vNOx=list(dfm.loc[dfm.spec=='NOx','sum_file'])[0]
  if vNOx==0.:sys.exit('vNOx==0')
  for s in spec:
    if spz[s]=='None': continue
    fac=list(dfm.loc[dfm.spec==s,'sum_file'])[0]/vNOx
    if s=='NOx': fac=1.
    fac=fac*1000./24
    f.variables[spz[s]][:,0,:,:]+=NOx[j0:j0+31,:,:]/area*fac
  f.close()

改變解析度(reso.py)

  • 由於mz2camx程式是採grab sample的方式,如果是線源、點源這類不連續場,倘若解析度差異很大,在切割時如果沒有被挑到,在質量上將會出現顯著的落差,因此除了原本的解析度(足夠d4)之外,還要預備給81K、27K解析度的檔案。 先執行前述nox_all2.py 產生path+’shp_east_asia’+mm+’.nc’ 後,再執行本程式。將會執行12個月、逐日排放量檔fnameO=fname.replace(‘.nc’,’_‘+str(xcell)+’.nc’) ,xcell=3 or 9。 以目前約9Km的解析度,還要再pile-up 3倍與9倍,以供d2及d1所用。
  • 準備_3與_9的經緯度座標值(line 15~23),由於此段是每個月都相同,只需要做一次即可。因降低解析度,網格範圍的東、北界限可能會損失,因此除了正規的範圍之外,另向外擴張2格,一共增加了5格。
  • 每月的迴圈line 24~先將所有5維的變數記下備用(line 30)
  • _3、_9不同解析度的迴圈(line 31~):切割出所要尺寸的模版(line 36)儲存前述預製好的經緯度座標值
  • 污染項目的迴圈(line 40~)
    • 用「疊影法」方式來進行網格的相加。雖然會多計算很多不必要的網格點,但「切勿」用j,i 迴圈逐一來做,會花很多時間。
    • 最後回存時用跳島式迴圈方式即可正確取到加總結果,回存時記得預留最外圈2層是擴張層。(可能會保留模版原有的數值)因為原檔案是按細網格面積來得到單位面積結果,加總後要更新基底面積(即為原來的xcell^2倍)
  • 注意nc檔案並不適用np.array的fancy indexing
kuang@114-32-164-198 /Users/TEDS/MACMIP/RadioForce8.5/ships
$ cat -n reso.py
     1
     2  import netCDF4
     3  import numpy as np
     4  import os
     5
     6  NCO='/cluster/netcdf/bin/'     #@master
     7  NCO='/opt/anaconda3/bin/'     #@IMac
     8  path=''
     9  fname=path+'shp_east_asia01.nc'
    10  nc = netCDF4.Dataset(fname,'r')
    11  V=[list(filter(lambda x:nc.variables[x].ndim==j, [i for i in nc.variables])) for j in [1,2,3,4]]
    12  nv=len(V[3])
    13  nt,nlay,nrow,ncol=nc.variables[V[3][0]].shape
    14  var=np.zeros(shape=(nv,nt,nrow,ncol))
    15  lat,lon={},{}
    16  for xcell in [3,9]:
    17    i3=ncol//xcell+1
    18    j3=nrow//xcell+1
    19    dx=nc.variables['lon'][xcell]-nc.variables['lon'][0]
    20    dy=nc.variables['lat'][xcell]-nc.variables['lat'][0]
    21    xorg,yorg=nc.variables['lon'][0]-dx*2,nc.variables['lat'][0]-dy*2
    22    lon.update({xcell:np.array([xorg+dx*i  for i in range(i3+4)])})
    23    lat.update({xcell:np.array([yorg+dy*j  for j in range(j3+4)])})
    24  for m in range(1,13):
    25    mm='{:02d}'.format(m)
    26    fname=path+'shp_east_asia'+mm+'.nc'
    27    nc = netCDF4.Dataset(fname,'r')
    28    for iv in range(nv):
    29      v=V[3][iv]
    30      var[iv,:,:,:]=nc.variables[v][:,0,:,:]
    31    for xcell in [3,9]:
    32      i3=ncol//xcell+5
    33      j3=nrow//xcell+5
    34      a3,b3=str(i3),str(j3)
    35      fnameO=fname.replace('.nc','_'+str(xcell)+'.nc')
    36      os.system(NCO+'ncks -O -d lat,1,'+b3+' -d lon,1,'+a3+' '+fname+' '+fnameO)
    37      nc1= netCDF4.Dataset(fnameO,'r+')
    38      nc1.variables['lat'][:]=lat[xcell]
    39      nc1.variables['lon'][:]=lon[xcell]
    40      for iv in range(nv):
    41        zz=var[iv,:,:,:]
    42        zz[:,1:-1,1:-1]=0.
    43        for j in range(1,xcell+1):
    44          for i in range(1,xcell+1):
    45            zz[:,:-j,:-i]+=var[iv,:,j:,i:]
    46        nc1.variables[v][:,0,2:-2,2:-2]=zz[:,:nrow:xcell,:ncol:xcell]/xcell/xcell
    47      nc1.close()
    48

後處理

  • (nc->m3->uamiv->cmaq_emis)
  • nc:等間距經緯度座標系統
  • m3:model3 IOAPI直角座標系統
  • uamiv: for CAMx simulation
  • cmaq_emis: for CMAQ simulation

nc2m3.cs

  • 此步驟將等間距經緯度座標系統,轉換成m3io之直角座標系統
  • 「shp_east_asia*.nc」會將所有檔案進行轉換,如果只要_3或_9檔案,可以予以明確化:
    • _3:     shp_east_asia*_3.nc
    • _9:     shp_east_asia*_9.nc
kuang@114-32-164-198 /Users/TEDS/MACMIP/RadioForce8.5/ships
$ cat nc2m3.cs
export EXECUTION_ID=mz2camx.job
export PROMPTFLAG=N
export IOAPI_ISPH=20

EXE=/Users/camxruns/src/mozart2camx_v3.2.1/ncf2ioapi_mozart/NCF2IOAPI
for res in 3 9;do
for i in $(ls shp_east_asia*_${res}.nc|cut -d'.' -f1) ;do
  export INFILE=${i}.nc
  export OUTFILE3D=${i}.m3.nc
  echo $i
  $EXE|tail -n5
done
done

par.cs

  • performed the nz2camx.job(切割domain)+(改成直角座標系統)
  • 由於mz2camx.job進行過程的輸入、輸出檔案名稱是固定的,一次只能進行一個月檔案的計算,因此如果要平行運作,必須開12個月的目錄分別進行計算。
  • 同理、d1~d4也不能同一目錄進行計算,必須分開計算,
  • 同時也必須限定最多同時執行的個數否則電腦CPU也不夠這麼多job同時計算
  • d1搭配_9(約81k)、d2搭配_3(約27k)、d3、d4則為原解析度
  • mz2camx.job的內容另外說明如下
  • mozart2camx 會將輸入內容按成分進行MMR->VMR(ppm)轉換
kuang@114-32-164-198 /Users/TEDS/MACMIP/RadioForce8.5/ships
$ cat par.cs
EXE=mz2camx
for d in 1 2;do for i in 0{1..9} 1{0..2};do mkdir -p ships${i}_$d;done;done
for d in 1 2;do for i in 0{1..9} 1{0..2};do cd ships${i}_$d;ln -sf ../output .;cd ..;done;done
for d in 1 2;do
  s=9
  test $d == 2 && s=3
  for i in 0{1..9} 1{0..2};do
      while true;do
        n=$(psg ${EXE}|wc -l)
        if [ $n -lt 12 ];then
          cd ships${i}_$d;sub ../mz2camx.job 15$i d$d ../shp_east_asia${i}_${s}.m3.nc >&mz2camx.log ;cd ..
          break
        else
          sleep 1s
        fi
      done
  done
done

mz2camx.job

#!/bin/csh -f
#$1->yymm
#$2->m3.nc
setenv PROMPTFLAG N
setenv IOAPI_ISPH 20
set EXE = /Users/camxruns/src/mozart2camx_v3.2.1/src/mozart2camx_CB6_CF__NCEP

set YYMM = $1
set d = $2
set OU = `echo $3|cut -d'.' -f1`
set MM = `echo $YYMM|cut -c 3-4`
set YY = `echo $YYMM|cut -c 1-2`
if ( $MM == 12) then
        @ YN   = $YY + 1
        @ YYMN = $YN * 100 + 1
else
        @ YYMN = $YYMM + 1
endif
if ( $MM == 01 ) then
        @ YP = $YY - 1
        @ YYMP = $YP * 100 + 12
else
        @ YYMP = $YYMM - 1
endif

set MET = /Users/WRF4.1/WRFv3/201909/wrfout/1909U$d

# DEFINE OUTPUT FILE NAMES
setenv EXECUTION_ID mz2camx.job
setenv OUTFILEBC  $YYMM$d".bc."$OU
mkdir -p ./output
foreach i ( 0 1 2 3 )
foreach j ( 0 1 2 3 4 5 6 7 8 9 )
if ( $i == '3' && $j > '1' ) goto BYPASS
set k = $i$j
if ( $k == '00' ) goto BYPASS
set DATE = "20"$YYMM$k
#set DATE = "0912"$k

# DEFINE INPUT MOZART FILES
# IF MORE THAN 1 MOZART FILE IS NEEDED, ADD setenv INFILE2
set NINFILE = 1
foreach t ( 00 )
foreach INFILE ($3)
setenv INFILE1 $INFILE
#if ( -e $OUTFILEIC ) goto BYPASS2

echo $DATE$t
set YYYYMMDD = $DATE
setenv OUTFILEIC  output/$YYYYMMDD$d"."$OU
echo $OUTFILEIC
rm -f $OUTFILEBC $OUTFILEIC
$EXE << IEOF
CAMx5,CAMx6,CMAQ   |CAMx 6
ProcessDateYYYYMMDD|$YYYYMMDD
Output BC file?    |.false.
Output IC file?    |.true.
If IC, starting hr |$t
Output TC file?    |.false.
Max num MZRT files |$NINFILE
CAMx 3D met file   |$MET.3d
CAMx 2D met file   |$MET.2d
IEOF
mv OUTFILEIC $OUTFILEIC
echo $INFILE1
BYPASS2:
      end
      end
BYPASS:
end
end
exit 0

mz2camx_node03.job

  • (only diff in $EXE and $MET locations, seems not used)
    $ diff mz2camx_node03.job mz2camx.job
    8c8
    < set EXE = ~/mac/camxruns/src/mozart2camx_v3.2.1/src/mozart2camx_CB6_CF__NCEP_node03
    ---
    > set EXE = /Users/camxruns/src/mozart2camx_v3.2.1/src/mozart2camx_CB6_CF__NCEP
    29c29
    < set MET = ~/mac/WRF4.1/WRFv3/201909/wrfout/1909U$d
    ---
    > set MET = /Users/WRF4.1/WRFv3/201909/wrfout/1909U$d
    

slm.cs

  • 運用pncgen 來修改uamiv檔案mz2camx結果會仿照WRF2CAMx結果的空間設定,因為是3維的,檔案過大必須縮減。uamiv格式可以使用pncgen切割維度(高度LAY)因為mozart基本上是處理空氣品質,在NAME的屬性是”AIRQUALITY”,必須改成”EMISSIONS “,否則CAMx會跳出來。
  • 可以在同一目錄下平行運作
EXE=pncgen
for D in d{1..4};do
  for da in 0{1..9} {10..31};
    do for mn in 0{1..9} 1{0..2};do
      while true;do
        n=$(psg ${EXE}|wc -l)
        if [ $n -lt 12 ];then
          fname=2015${mn}${da}${D}.
          fnameo=${fname}L
          sub ${EXE} --format=uamiv -O --out-format=uamiv --slice=LAY,0 -a NAME,global,o,c,'EMISSIONS ' $fname $fnameo>&/dev/null
#         sleep 1s
          break
        else
          sleep 1s
        fi
      done
    done
  done
done

cbl.cs

  • 將逐日、地面檔案組合成月檔案。結果會是2015${m}.emis.grd0${d},uamiv檔案,可以直接加入CAMx模擬、或經camx2cmaq轉換後加到CMAQ排放量檔案裏。
  • 有暫存檔,「不能」在同一目錄平行運作
for d in 1 2 3 4;do
  for m in 0{1..9} 1{0..2};do
    cbin_all "2015${m}??d${d}.L" "2015${m}.emis.grd0${d}">&/dev/null
  done
done

cbin_all 為傳統uamiv檔案的連接程式,功能與ncrcat之基本功能相同,詳見 https://github.com/sinotec2/CAMx_utility/blob/master/cbin_avrg.par.f

add_Ship.py

  • 加入既有的area.nc 檔案
    • 執行範例:add_SO20.py 01 02(1601月第2層網格)
    • 逐月檔案之單位尚未轉換成正確單位,仍為mz2camx之結果(PPM),除了分子量外,其餘皆需還原。(fac, line35)
    • 由於mar_path+’2015’+mo+’.emis.grd’+gd 為d0範圍(5959),與d1(5353)差了6格,分別由四個方向的外圈扣除。(line66~68、78、80)
    • 結果檔案名稱為’fortBE.’+gd[1]+’13.teds10.base’+mo+’S.nc’,S for Ship(line 54)
    • NO2佔NOx重量的1/10、NO佔NOx重量的9/10
    • 因資料庫涵括了內陸水運的排放,對臺灣地區而言,似為干擾而非貼近實況,需要去除,因此導入gridmask中對海域之定義(line 41~51)只在d04中反映,其他範圍解析度不考慮d04會有頭尾時間不足的問題,在此一併檢查處理。
kuang@master /nas1/cmaqruns/2016base/data/emis
$ cat -n /home/kuang/mac/cmaqruns/2016base/data/emis/add_Ship.py
     1  #!/cluster/miniconda/envs/py37/bin/python
     2  import numpy as np
     3  import netCDF4
     4  import os,sys,subprocess
     5  from PseudoNetCDF.camxfiles.Memmaps import uamiv
     6  import datetime
     7
     8  def dt2jul(dt):
     9    yr=dt.year
    10    deltaT=dt-datetime.datetime(yr,1,1)
    11    deltaH=int((deltaT.total_seconds()-deltaT.days*24*3600)/3600.)
    12    return (yr*1000+deltaT.days+1,deltaH*10000)
    13
    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
    20
    21  root=subprocess.check_output('pwd',shell=True).decode('utf8').strip('\n').split('/')[1]
    22  mar_path='/'+root+'/TEDS/en.ilmatieteenlaitos.fi/surveying-maritime-emissions/output/'
    23  km2={'01':81*81,'02':27*27,'04':3*3}
    24  mo=sys.argv[1];gd=sys.argv[2]
    25  fname=mar_path+'2015'+mo+'.emis.grd'+gd
    26  marf = uamiv(fname,'r')
    27  V=[list(filter(lambda x:marf.variables[x].ndim==j, [i for i in marf.variables])) for j in [1,2,3,4]]
    28  Vm=[]
    29  for x in V[3]:
    30    if x in ['SO2','NO2','NO']:continue
    31    if np.sum(marf.variables[x][:])==0.:continue
    32    Vm.append(x)
    33  ntm,nlaym,nrowm,ncolm=marf.variables['SO2'].shape
    34  #yjhm=[i*100+int(j/10000) for i,j in zip(marf.variables['TFLAG'][:,0,0],marf.variables['TFLAG'][:,0,1])]
    35  fac=28.9644*10**6/km2[gd]# (gmole/hour)
    36  SO2=np.array(marf.variables['SO2'][:,:,:,:])/fac
    37  NO2=np.array(marf.variables['NO2'][:,:,:,:])/fac *0.1
    38  NO=np.array(marf.variables['NO2'][:,:,:,:])/fac *0.9
    39  
    40  marine=np.ones(shape=(nrowm,ncolm))
    41  if gd=='04':
    42    fnameL='/'+root+'/cmaqruns/2016base/data/land/epic_festc1.4_20180516/gridmask/TWN_CNTY_3X3.nc'
    43    ncL = netCDF4.Dataset(fnameL,'r+')
    44    V=[list(filter(lambda x:ncL.variables[x].ndim==j, [i for i in ncL.variables])) for j in [1,2,3,4]]
    45    marine=marine*0.
    46    for v in V[3]:
    47      if len(v)==2:
    48        marine[:,:]+=ncL.variables[v][0,0,:,:]
    49    idx=np.where(marine==0.) #take the marine indices
    50    marine=marine*0.         #reset the matrix
    51    marine[idx[0],idx[1]]=1. #only marine grids are one's
    52
    53  fname='fortBE.'+gd[1]+'13.teds10.base'+mo+'.nc'
    54 fnamO='fortBE.'+gd[1]+'13.teds10.base'+mo+'S.nc'
    55  os.system('cp '+fname+' '+fnamO)
    56  nc = netCDF4.Dataset(fnamO,'r+')
    57  V=[list(filter(lambda x:nc.variables[x].ndim==j, [i for i in nc.variables])) for j in [1,2,3,4]]
    58  nt,nlay,nrow,ncol=nc.variables['SO2'].shape
    59  nv=len(V[3])
    60  ib,jb=0,0
    61  if nlay!=nlaym or ncol!=ncolm or nrow!=nrowm:
    62    if nlay!=nlaym:sys.exit('shape not right')
    63    if nrowm>nrow:jb=int((nrowm-nrow)/2)
    64    if ncolm>ncol:ib=int((ncolm-ncol)/2)
    65    SO2=SO2[:,:,jb:-jb,ib:-ib]
    66    NO2=NO2[:,:,jb:-jb,ib:-ib]
    67    NO =NO [:,:,jb:-jb,ib:-ib]
    68    marine=marine[jb:-jb,ib:-ib]
    69  #yjh=[i*100+int(j/10000) for i,j in zip(nc.variables['TFLAG'][:,0,0],nc.variables['TFLAG'][:,0,1])]
    70  for t in range(nt):
    71    it=int(t/24)%ntm
    72    nc.variables['SO2'][t,:,:,:]+=SO2[it,:,:,:]*marine[:,:]
    73    nc.variables['NO' ][t,:,:,:]+=NO [it,:,:,:]*marine[:,:]
    74    nc.variables['NO2'][t,:,:,:]+=NO2[it,:,:,:]*marine[:,:]
    75    for v in Vm:
    76      if v not in V[3]:continue
    77      if ib+jb==0:
    78        nc.variables[v][t,:,:,:]+=np.array(marf.variables[v][it,:,:,:]*marine[:,:])/fac
    79      else:
    80        nc.variables[v][t,:,:,:]+=np.array(marf.variables[v][it,:,jb:-jb,ib:-ib]*marine[:,:])/fac          
    81  #checking the beginning STIME, must begion at 0 UTC
    82 dend=jul2dt(list(nc.variables['TFLAG'][nt-1,0,:]))
    83  tb=int(nc.STIME/10000)
    84  if tb!=0:
    85    nc.STIME=0.
    86    var=np.zeros(shape=(nv,nt,nrow,ncol))
    87    for iv in range(nv):
    88      var[iv,:,:,:]=nc.variables[V[3][iv]][:,0,:,:]
    89    tflag=nc.variables['TFLAG'][:,0,:]
    90    for t in range(0,tb):
    91      nc.variables['TFLAG'][t,:,0]=[nc.SDATE for i in range(nv)]
    92      nc.variables['TFLAG'][t,:,1]=[t*10000. for i in range(nv)]
    93      tt=t-tb+24
    94      for iv in range(nv):
    95        nc.variables[V[3][iv]][t,0,:,:]=var[iv,tt,:,:]
    96
    97    for t in range(tb,tb+nt):
    98      tt=t-tb
    99      nc.variables['TFLAG'][t,:,0]=[tflag[tt,0] for i in range(nv)]
   100      nc.variables['TFLAG'][t,:,1]=[tflag[tt,1] for i in range(nv)]
   101      for iv in range(nv):
   102        nc.variables[V[3][iv]][t,0,:,:]=var[iv,tt,:,:]
   103
   104  #check the last day or run12
   105  mo=int(mo)
   106  lastYr=(datetime.datetime(2016,mo,1)+datetime.timedelta(days=-1)).year
   107  lastMo=(datetime.datetime(2016,mo,1)+datetime.timedelta(days=-1)).month
   108  begd=datetime.datetime(lastYr,lastMo,15)
   109  endd=begd+datetime.timedelta(days=48)
   110  if endd<=dend:          #in case of more than 48 days(run12)
   111    nc.close()
   112    sys.exit('endj check ok')
   113  else:                   #duplicating the last day
   114    delH=int((endd-dend).total_seconds()/3600.)+1
   115    for h in range(delH):
   116      SDATE=dt2jul(dend+datetime.timedelta(days=+(h+1)/24.))
   117      t=nt+h+tb
   118      for j in range(2):
   119        nc.variables['TFLAG'][t,:,j]=[SDATE[j]  for i in range(nv)]
   120      tt=t-24
   121      for k in range(5):  #prevention greater than nt
   122        if tt>=nt:tt=tt-24
   123      for iv in range(nv):
   124        nc.variables[V[3][iv]][t,0,:,:]=var[iv,tt,:,:]
   125    nc.close()

排放量之調整

  • 船舶排放量除NO2之外,其餘污染項目為NO2之一定比例來估算,因此不確定性很高,調整時可以用python進行一次性之乘除,如下:
    • d1、d2模擬結果比對,SO2的GE約是1.8~2.8,表示原排放量需除2.8~3.8。
from PseudoNetCDF.camxfiles.Memmaps import uamiv
fname='fortBE.113_STEAM.base01_S1.8'
nc= uamiv(fname,'r+')
nc.variables['SO2'][:]=nc.variables['SO2'][:]/2.8
nc.close()
fname='fortBE.213_STEAM.base01_S2.8'
nc.variables['SO2'][:]=nc.variables['SO2'][:]/3.8
nc.close()

檢視成果

1月份平均NOx排放量及run5平均SO2空品模擬結果。

Reference

  • Lasse Johansson, Jukka-Pekka Jalkanen, Jaakko Kukkonen, Global assessment of shipping emissions in 2015 on a high spatial and temporal resolution, Atmospheric Environment
  • CAMx(UAM)的檔案格式, Yungchuan Kuang edited this page on 12 Jul 2016 · 2 revision, shttps://github.com/sinotec2/camxruns/wiki/CAMx(UAM)的檔案格式
  • 發表文獻STEAM_reference_list_update_22012018

Appendix

  • Convert mass mixing ratio (MMR) to mass concentration or to volume mixing ratio (VMR) confluence.ecmwf Ship Traffic Emission Assessment Model (STEAM)
For O<sub>3</sub>, the formula is: VMR = 28.9644 / 47.9982 * 1e6 * MMR
For CO the formula is: VMR = 28.9644 / 28.0101 * 1e6 * MMR
For NO2 the formula is: VMR = 28.9644 / 46.0055 * 1e6 * MMR
For SO2 the formula is: VMR = 28.9644 / 64.0638 * 1e6 * MMR
For CO2 the formula is: VMR = 28.9644 / 44.0095 * 1e6 * MMR
For CH4 the formula is: VMR = 28.9644 / 16.0425 * 1e6 * MMR
  • mz2camx會將檔案內容乘上10^6(粒狀物乘上10^9),因此必須在某個時間點將其除回來。
  • 理論上CDO可以達成作業要求,但似力有未逮。最後仍然適用python程式完成此項作業。
CDO=/usr/local/bin/cdo
YY=15
for MM in 0{1..2};do # 151{0..2};do
  nc=shp_east_asia$MM.m3.nc
  $CDO -O selvar,OC1,BC1 $nc tmp1.nc
  $CDO -O divc,1000000000 tmp1.nc tmp2.nc
  $CDO -O replace $nc tmp2.nc tmp3.nc

  $CDO -O selvar,BIGENE,C3H8,BENZ,C2H6,C2H4,CH4,C3H6,BIGALK,CO,TOLUENE,XYL,NO2,BIGALD,SO2,C4H10 $nc tmp1.nc
  $CDO -O divc,1000000 tmp1.nc tmp2.nc
  $CDO -O replace tmp3.nc tmp2.nc tmp4.nc

  for D in d1 d2;do
    YYMM=$YY$MM
    mz2camx.job $YYMM $D tmp4.nc
  done
done