Link Search Menu Expand Document

MMIF之遠端執行系統

Table of contents

背景

遠端系統的建置,包括站台設置、html(js)程式、CGI_python程式、以及OS批次檔,前3者詳見網頁計算服務軟體系統的建置之說明,此處集中在批次執行說明。

由於mmif時間範圍經年,執行時間會超過瀏覽器停等時間(造成瀏覽器time out),CGI及mmif程式都會停擺。mmif程式必須轉到背景執行與CGI脫勾才能順利完成。因應此一需求,有以下3個作法:

  1. 以自動執行之批次檔案承接,結束後以email通知
    • 此一版本的背景執行交由crontab來管控
    • 適用在頻繁呼叫、頻繁起、停的情況(課堂習作)
  2. 直接在cgi_python內啟動背景執行,由使用者自行上網確認結果
  3. 預先以工作站完成各年度WRF在各網格之MMIF轉檔,再由網頁或(及)CaaS系統提供

CaaS網址為:http://sinotec24.com/mmif.html@iMacKuang1

自動執行搭配版本

除了伺服器端的CGI程式之外,實際執行分2個伺服器身份進行,cybee為計算執行者,root則為檔案管理者,由crontab進行自動執行之管控。使用者只需將準備好的mmif.inp上傳到伺服器,填寫正確的回函    email 信箱,系統就會將執行結果的連結寄給使用者。

系統元件

  1. 網址為:http://sinotec24.com/mmif.html,屬於個人負責營運維護的伺服器系統。
    • select a file:選擇要執行的mmif.inp檔案。檔名必須是小寫、名字完全相符,不接受壓縮檔。設定方式詳下述。
    • EMAIL:輸入回函email 信箱。系統不會寄發檢核信件。
    • Upload and Run mmif remotely:上傳檔案到伺服器上、並等候crontab來執行mmif。
  2. 回函:
    • 系統寄件者名稱為cybee、主旨分別為MMIF ACCEPTED及MMIF RESULT。
    • 第一封回函會在開始執行程式之前寄到信箱,視伺服器壅塞的程度而異,內設執行批次個數少於6時,會接受新的批次。
    • 第二封回函會在完成執行後寄達。視指定mmif執行模擬的時間長短而異,時間長、批次多,等候的時間就會很久。
    • 點選連結會自動下載到使用者的“下載”目錄,只要用一般解壓縮軟體就能打開。
    • 如果沒有收到回函,有可能:
    1. 前述正常的等候時間
    2. 被當成垃圾信
    3. 如果垃圾箱沒有,請確認email是否有誤
  3. 特色及好處:
    • 不需在使用者電腦上編譯mmif的執行檔。USEPA並無義務提供任意平台的程式執行檔。
    • 不需登入主機、不用登記會員、不做cookie紀錄追蹤、不佔用電腦資源。
    • 輕量化系統。由於潛在使用者並不多,系統沒有經常性的執行壓力。
    • 使用CWB的WRF結果(中央氣象局WRF_3Km數值預報產品之下載及轉檔),有一定的公信力。
  4. 待改進處:
    • 目前只有2020年WRF數據、且CWB有缺漏情形。
    • 目前只提供台灣本島範圍的經緯度座標,外島部分尚待開發。
    • mmif.inp 的準備,尚涉及aermod模式模擬時間、空間範圍的設定,有待建置互動界面以減少錯誤。
    • 伺服器計算速度以及網路頻寬有待提升。

cybee crontab contents

  • 執行頻率
    • doing per min..
*/1 * * * * /Users/cybee/mmif_cron.cs >& /Users/cybee/mmif_cron.out
  • 執行內容(計算啟動之檢查、控制、執行、郵寄)
    • /Users/cybee/mmif_cron.cs contents
      • 執行邏輯
        1. 電腦負荷太多時、跳開不執行(設定為6個mmif工作)(line 6)
        2. 使用者開了目錄(隨機產生名字),才能執行。開了目錄的2天後將不會存在。
        3. 依照目錄順序進行檢查、執行:(line 11)
        4. 如果有email但是還沒有寄過ACCEPTED第一封信,則執行寄信動作。指標為emai_bak檔案是否存在(line 14~22)。
        5. 目錄下如果有mmif.inp檔案、卻沒有mmif.out檔案,則開始執行程式、製作成果壓縮檔、寄發第二封信RESULT。(line 24~39)
        6. 下一個目錄
  1  
  2  #ensure not to many mmif are running
  3  n=$(ps -ef|grep /opt/local/bin/mmif|wc -l)
  4  n=$(( $n - 1 ))
  5  if [ $n -le 6 ];then
  6  
  7  #ensure mmif.inp was put to the directory
  8  m=$(ls /tmp|grep mmif_|wc -l)
  9  if [ $m -gt 0 ];then
10  
11  for dr in $(ls /tmp|grep mmif_);do
12  dir=/tmp/$dr
13  #ensure email was given
14  if ! [ -e $dir/mmif.email_bak ] && [ -e $dir/mmif.email ];then
15  #send first email     
16      emailadd=$(cat $dir/mmif.email)
17      echo "Hello MMIF user:\n Your mmif submit was accepted, please wait and check this email.\
18    since $n mmif are running    
19    The resultant tgz file will send to you and the file will be erased after 24 hrs! \n \
20    (sent by machine do not reply)" | mail -s "MMIF ACCEPTED" $emailadd
21      cp $dir/mmif.email $dir/mmif.email_bak
22  fi
23  
24  #ensure mmif.inp exist and perform mmif
25  if [ -e $dir/mmif.inp ];then
26      if ! [ -e $dir/mmif.out ];then
27        cd $dir
28        cp mmif.inp mmif.recieved
29        /opt/local/bin/mmif>mmif.out
30        /usr/bin/zip result.zip *
31    emailadd=$(cat $dir/mmif.email)
32        echo "Hello MMIF user:\n Your mmif result was at http://sinotec24.com$dir/result.zip\n \
33      Please fetch the file as soon as possible,\n \
34          The file will be erased after 24 hrs!\n \
35      (sent by machine do not reply)" | mail -s "MMIF RESULT" $emailadd
36  #        for i in $(ls|grep -v result.zip);do rm -f $i;done
37  #      rm -f $dir/result.zip|at now+24 hours
38      fi
39  fi
40  done
41  fi
42  fi

root crontab contents

  • 執行頻率
    • doing per day.
  • 執行內容(檔案管理)
    • 執行成果、設定必須定期清理,避免硬碟消耗。此處設定為24小時後,考慮到時間差,最長為2日。
    • 必須由root來清理的理由是使用者(_www)開啟的目錄、檔案,即使由CGI_python程式宣告屬性為777,仍然不能被其他一般使用者(如mmif的實際執行者cybee)刪除。
    • macOS的ls沒有ISO格式,必須使用gls (詳)
    • MacOS的date也有差異。
      #remove the mmif_* 2 days ago
      0 0  *  *  * cd /tmp;dd=$(/bin/date -j -v-2d +"%Y-%m-%d");for i in $(/usr/local/bin/gls -l --time-style=full-iso|grep mmif_|grep $dd|/opt/local/bin/awkk 9);do rm -fr $i;done
      

cgi_python內啟動背景執行

此版本適用在少數零星之mmif作業,此時如果維持一固定(高度)頻率的檢查、由系統自動執行批次、似無理由。只要使用者不離開網頁、或保留連結資訊,只待程式執行完後自行上網下載成果,也十分方便自在。

mmif.inp之準備

此版本允許使用者輸入kml檔案,以指定欲執行mmif位置之經緯度,因此需要一模版,用sed來置換其內的經緯度、檔案名稱等重要訊息。

模版內容:

$ cat -n mmif.inp_blank20|more
  1  start      2020 01 01 08
  2  stop       2021 01 01 08 
  3  TimeZone   +8
  4  grid       IJ -5,-5 -5,-5
  5  layers top 20 40 80 160 320 640 1200 2000 3000 4000
  6  stability  GOLDER
  7  PBL_recalc FALSE
  8  aer_min_speed 0.5
  9  aer_min_mixht 1.0
10  aer_min_obuk  1.0
11  POINT  LL         LATI LONG
12  AER_layers        1        4
13  Output aermet     useful   run_aermet_xiehe.csh
14  Output aermet     onsite   xiehe.dat
15  Output aermet     upperair xiehe.fsl
16  Output aermet     aersfc   xiehe.aersfc.dat
17  FSL_INTERVAL      6
18  POINT  latlon     LATI LONG      8
19  Output aermet     FSL      'Upper_air_at_xiehe.FSL'
20  POINT  latlon     LATI LONG      8
21  AER_layers        0        0
22  Output aermod     useful   xiehe.info.txt
23  Output aermod     sfc      xiehe.sfc
24  Output aermod     PFL      xiehe.pfl
25  INPUT /nas1/backup/data/CWB_data/raw/20191231/wrfout_d04_2019-12-31_06:00:00
26  INPUT /nas1/backup/data/CWB_data/raw/20200101/wrfout_d04_2020-01-01_06:00:00
27  INPUT /nas1/backup/data/CWB_data/raw/20200102/wrfout_d04_2020-01-02_06:00:00
28  INPUT /nas1/backup/data/CWB_data/raw/20200103/wrfout_d04_2020-01-03_06:00:00
29  INPUT /nas1/backup/data/CWB_data/raw/20200104/wrfout_d04_2020-01-04_06:00:00
...

其中

  1. 經緯度(LATI、LONG)(line 11, 18, 20 ):後續在執行CGI_python時以sed修改
  2. 結果檔案名稱(line 23~24):後續執行時以sed修改

CGI_Python程式

  • 客戶如果提供的是mmif.inp,複製一份到工作目錄下。(line 31~32)
  • 如客戶提供的是kml檔,先讀取個案名稱與座標,以sed由模版複製一份,並以sed置換其中經緯度與檔名,準備好mmif.inp。(line 33~42)
  • 在背景執行mmif(line 43~49)
  • 提供結果檔案之網址,待執行完成後由客戶自行下載。
kuang@114-32-164-198 /Library/WebServer/CGI-Executables/isc 
$ cat -n mmif.py 
  1  #!/opt/anaconda3/envs/py27/bin/python 
  2  # -*- coding: UTF-8 -*- 
  3 
  4  import cgi, os, sys 
  5  import cgitb; cgitb.enable() 
  6  import tempfile as tf 
  7  import subprocess 
  8  from pykml import parser 
  9  from rd_kmlLL import rd_kmlLL 
10 
11  form = cgi.FieldStorage() 
12 
13  mmif_parm=['AER_MIN_OBUK', 'POINT', 'AER_LAYERS', 'AER_MIN_MIXHT', 'INPUT', 'GRID', 'TIMEZONE', 'PBL_RECALC', 'AER_MIN_SPEED', 'OUTPUT', 'STOP', 'FSL_INTERVAL', 'LAYERS', 'STABILITY', 'START'] 
14  mmif_parm=set(mmif_parm) 
15  ran=tf.NamedTemporaryFile().name.replace('/','').replace('tmp','') 
16  # 獲取檔名 
17  fileitem = form['filename'] 
18  MMIF='/opt/local/bin/mmif' 
19  WEB='/Library/WebServer/Documents/' 
20  CGI='/Library/WebServer/CGI-Executables/isc/' 
21  pth=WEB+'isc_results/mmif_'+ran+'/' 
22  OUT='>> '+pth+'isc.out' 
23  SED='/usr/bin/sed -ie' 
24  os.system('mkdir -p '+pth) 
25  print 'Content-Type: text/html\n\n' 
26  print open(CGI+'header.txt','r') 
27 
28  # 檢測檔案是否上傳 
29  if fileitem.filename: 
30    # 設定檔案路徑 
31    fn = os.path.basename(fileitem.filename) 
32    open(pth+fn, 'wb').write(fileitem.file.read()) 
33    if 'kml' in fn: 
34      case,lon,lat=rd_kmlLL(pth+fn) 
35      print case+(' {:f} {:f} </body></html>').format(lon,lat) 
36      cmd ='cd '+pth+';' 
37      cmd+='cp ../mmif.inp_blank mmif.inp;' 
38      cmd+=SED+' "s/LATI/{:f}/g" mmif.inp'.format(lat)+';' 
39      cmd+=SED+' "s/LONG/{:f}/g" mmif.inp'.format(lon)+';' 
40      cmd+=SED+' "s/xiehe/{:s}/g" mmif.inp'.format(case) 
41      os.system('echo "'+cmd+'"'+OUT) 
42      os.system(cmd) 
43    sfc=subprocess.check_output('grep " sfc " '+pth+'mmif.inp|awk "{print \$NF}"',shell=True).decode('utf8').strip('\n').strip('\r') 
44    ext=sfc.split('.')[-1] 
45    case=sfc.replace(ext,'') 
46    cmd ='cd '+pth+';' 
47    cmd+=MMIF+'|grep Hourly|grep written>'+case+'out &' 
48    os.system('echo "'+cmd+'"'+OUT) 
49    os.system(cmd) 
50  if not os.path.exists(case+'sfc') or not os.path.exists(case+'pfl'): 
51    r=os.system(cmd) 
52    if r!=0: 
53      print """Something wrong in AERMOD excutions, see <a data-auto-download href="%s">%s</a> 
54      <body></html> 
55      ename=pth+case+'out' 
56      """  % (ename.replace(WEB,'../../../'),ename.split('/')[-1]) 
57      sys.exit() 
58 
59  pid=subprocess.check_output('ps -ef|grep aermod|head -n1|/opt/local/bin/awkk 2',shell=True).decode('utf8').strip('\n') 
60  print 'mmif is running at pid= '+pid+'</br>' 
61  os.system('sleep 30s') 
62  fnames=subprocess.check_output('ls '+pth,shell=True).decode('utf8').strip('\n').split() 
63  print """\ 
64    MMIF_results: The MMIF process will take hours. You may check them during program excution:</br> 
65    DO NOT RELOAD this web-page !!!</br> 
66    """ 
67 
68  for fn in fnames: 
69    fname=pth+fn 
70    print """\ 
71    <a data-auto-download href="%s">%s</a></br> 
72    """  % (fname.replace(WEB,'../../../'),fname.split('/')[-1]) 
73  print '</body></html>' 
74  sys.exit()

副程式rd_kmlLL.py

此副程式應用pykml之parser模組,由kml檔案中讀取Points的名稱及位置。詳rd_kmlLL.py

全台網格預處理版本

有鑒於在macOS上執行mmif需時較長(主要受限gfortran與netcdf的能力不佳),考慮以工作站批次執行mmif(ifort版本)網格轉檔,以減省macOS的執行時間。

執行構想

  • WRF年代:以工作站目前已有成果為基準。由於風險評估所需模擬至少需要完整的5年,恰等於目前2016~迄今之5年。
  • 空間範圍與解析度:
    • 考量TWN_3X3 (D4範圍解析度)
    • 台灣地區並非所有地方均能(適合)開發污染源,此處就已經開發工廠之地區為考量,自TEDS10點源資料庫中讀取點位。其它點位則由使用者啟動MMIF來轉檔。
    • 由於一般AERMOD模擬範圍約3~30Km範圍,在D4範圍內有1~10格,只能取一處氣象數據做為代表。就此而言,3Km之解析度應屬充分足夠。
    • 在WRF系統內,小於此一空間範圍亦無其他訊息,無法提供更小解析度之數據。
  • 平行運作:
    • mmif雖然沒有平行計算的能力,但如能將執行批次分開創建目錄,將程式的IO檔案分目錄存放,批次之間將不會彼此干擾可以平行作業。
    • 運用動態控制執行緒數的原理(詳entry linux*),可以在os平台上實現平行運作。

空間點位之歸納

自point_QC.csv(詳點源排放檔案準備或早期read_point.py版本)中歸納出台灣地區已設有工廠的網格位置,另存成point_ij.csv,其表頭如下:

kuang@114-32-164-198 /Users/1.PlumeModels/AERMOD/mmif/TWN_3X3 
$ head point_ij.csv 
IJ,X,Y,lon,lat 
7152,-102000.0,252000.0,119.936,25.961 
7159,-102000.0,273000.0,119.934,26.157 
8159,-99000.0,273000.0,119.965,26.157 
11048,-90000.0,-60000.0,120.082,23.047 
12048,-87000.0,-60000.0,120.112,23.047 
12054,-87000.0,-42000.0,120.111,23.215 
13047,-84000.0,-63000.0,120.142,23.019 
13048,-84000.0,-60000.0,120.142,23.047 
13053,-84000.0,-45000.0,120.141,23.187
...
  • IJ = I*1000 + J,為東西、南北向網格標籤的組合
    • 每網格分開、分批作業,具有獨立性
    • 將可作為檔案目錄、命名之依據
  • X,Y:lambert projection座標值,原點在台灣中心點

使用整併程式如下

  • 先求出點源所在位置的IJ(整數化)、再以pivot_table取平均。因同一網格中心的XY值都相同,平均值即為取集合之代表值。
  • 經緯度取3位截尾,以使在mmif.inp檔案內置換後有較佳的可讀性
  • (83,137)為d4範圍之網格數
kuang@114-32-164-198 /Users/1.PlumeModels/AERMOD/mmif/TWN_3X3 
$ cat point_ij.py 
from pyproj import Proj 
import numpy as np 
from pandas import * 
Latitude_Pole, Longitude_Pole = 23.61000, 120.990 
pnyc = Proj(proj='lcc', datum='NAD83', lat_1=10, lat_2=40, 
        lat_0=Latitude_Pole, lon_0=Longitude_Pole, x_0=0, y_0=0.0) 
df=read_csv('point_QC.csv') 
df['II']=[int(i/3000+83/2) for i,j in zip(df.UTM_E,df.UTM_N)] 
df['JJ']=[int(j/3000+137/2) for i,j in zip(df.UTM_E,df.UTM_N)] 
boo=(df.II>=0) & (df.JJ>=0) 
dfij=df.loc[boo].reset_index(drop=True) 
dfij['IJ']=dfij.II*1000+dfij.JJ 
dfij['X']=(dfij.II- 83/2)*3000+1500 
dfij['Y']=(dfij.JJ-137/2)*3000+1500 
pv=pivot_table(dfij,index='IJ',values=['X','Y'],aggfunc=np.mean).reset_index() 
X,Y=np.array(pv.X),np.array(pv.Y);lon, lat= pnyc(X,Y, inverse=True) 
pv['lon']=[round(i,3) for i in lon] 
pv['lat']=[round(i,3) for i in lat] 
pv.set_index('IJ').to_csv('point_ij.csv')

mmif.inp樣版之準備

與前述樣版一致,作法略異

  1. 年度起迄時間(line 1~2):手工修改
  2. 經緯度(LATI、LONG)(line 11, 18, 20 ):後續執行批次以sed修改
  3. 結果檔案名稱(line 23~24):後續執行批次以sed修改
  4. 各年度WRF檔案路徑之收集(line 25~)
    • 先將各批次執行成果蒐集在各月份的wrfout下,再彙總到$btn/links下(詳WRF三維軌跡與叢集分析*)
    • 由於2020年係來自CWB WRF_3Km(中央氣象局WRF_3Km數值預報產品之下載及轉檔*)結果,有不同目錄,須分別處理。
    • 形成fname.txt_yy之後,再手工加入前後跨年度檔案路徑。
    • 將fname.txt_yy貼在mmif.inp_blank之後即可
for y in {16..19};do for i in {01..12};do for j in $(ls /nas1/backup/data/cwb/e-service/btraj_WRFnests/links/wrfout_d04_20${y}-${i}*);do echo INPUT $j;done;done>fname.txt_$y done
for i in {01..12};do for j in $(ls /nas1/backup/data/CWB_data/raw/*/wrfout_d04_2020-${i}*);do echo INPUT $j;done;done>fname.txt_20

平行運作之批次檔(do_mmif.cs)

運用動態控制執行緒數的原理(詳entry linux),讓工作站所有CPU同時進行各格點MMIF的轉檔工作。批次檔內容及說明如下:

kuang@master /nas1/aermruns/mmif 
$ cat -n do_mmif2.cs 
  1 
  2  MMIF=/nas1/aermruns/mmif/src/mmif 
  3  for y in {16..20};do 
  4  for LINE in $(cat point_ij.csv|grep -v IJ);do 
  5    IJ=$(echo $LINE|cut -d',' -f1) 
  6    LONG=$(echo $LINE|cut -d',' -f4) 
  7    LATI=$(echo $LINE|cut -d',' -f5) 
  8    n=$(psg $MMIF|wc -l) 
  9    while true;do
10      if [ $n -lt 90 ];then 
11        mkdir -p 20$y/$IJ 
12        cd 20$y/$IJ 
13        cp ../../mmif.inp_blank$y mmif.inp 
14        for cmd in 's/xiehe.sfc/IJ'$IJ'.sfc/g' 's/xiehe.pfl/IJ'$IJ'.pfl/g' \ 
15          's/LONG/'$LONG'/g' 's/LATI/'$LATI'/g';do 
16          sed -i $cmd mmif.inp 
17        done 
18        sub $MMIF >& dum 
19        cd ../.. 
20        sleep 5s 
21        break 
22      else 
23        sleep 5s 
24        n=$(psg $MMIF|wc -l) 
25      fi 
26    done 
27  done 
28  done 
29
  • 主要3層迴圈,由外而內依序為:WRF年代(y line3~28)、每格點(LINE line4~27)、與動態檢核CPU執行緒數n之while迴圈(keep n<90, line 9~26)
  • 網格IJ、經緯度等訊息藉由echo cut來傳到批次檔內
  • 每執行批次所需的mmif.inp,以sed來替換(line 13~17),置換之邏輯與前述版本CaaS相同。
  • 啟動背景執行後隨即關閉while迴圈

執行成果

  • 放在$web/mmif_results/20yy下,以供使用者在uMap下載(詳地圖上貼連結)、及(或)其他應用調用(主要是aermods.html或AERMOD.html)。
  • 降雨量:
  • CWB_WRF資料缺漏補遺(mod_Times.py)

點位選取方式

  1. 傳統輸入mmif.inp檔案。直接寫在檔案內。
  2. 以uMap上點選,直接下載成果。
  3. 輸入工廠建築物煙囪之KML檔案,由電腦選取結果供下載。

Reference

Family

  1. sinotec24.com為Hinet給定,如遇機房更新或系統因素,將不會保留。使用者敬請見諒,逕洽作者:sinotec2@gmail.com.