> 文章列表 > WRF自动运行脚本/Shell进阶:Shell脚本自动修改namelist.input日期与自动任务提交

WRF自动运行脚本/Shell进阶:Shell脚本自动修改namelist.input日期与自动任务提交

WRF自动运行脚本/Shell进阶:Shell脚本自动修改namelist.input日期与自动任务提交

由于WRF本身属于中尺度天气模式,其模拟时间尺度不宜过长,当我们想要准确地进行模拟时,时间不宜大于10天,当我们要进行长期模拟时,不宜直接进行长期的时间设定,最好是以一定的时间间隔重新修改namelist.input的开始于end日期。即,若想要模拟一年的数据,则应每隔以段时间(如五天)重新提交任务,即1月1日-1月6日模拟一次,随后1月6日至一月10日模拟。
如果模拟时期较长,则需要反复多次修改namelist.input中的&time_control,重新提交任务。
这种反复重复的操作我们可以使用脚本自动化解决,在linux下,我们有必要学习相关的shell脚本。

sed

在linux中,存在着编辑替换文件内容的命令sed,其命令格式与选项为:

sed的命令格式:sed [options] 'command' file(s);sed的脚本格式:sed [options] -f scriptfile file(s);-e :直接在命令行模式上进行sed动作编辑,此为默认选项;-f :将sed的动作写在一个文件内,用–f filename 执行filename内的sed动作;-i :直接修改文件内容;-n :只打印模式匹配的行;-r :支持扩展表达式;-h或--help:显示帮助;-V或--version:显示版本信息。

其替换文件指定内容的命令可以写作:

sed -i 's/Search_String/Replacement_String/g' Input_File

通过该命令,我们可以将指定文件内的指定内容进行替换。

修改namelist.input

namelist.input中,我们需要修改的部分主要是:

start_year = 2019, 2019, 2019,start_month = 01, 01, 01,start_day = 05, 05, 01,start_hour = 00, 00, 00,end_year = 2019, 2019, 2019,end_month = 01, 01, 01,end_day = 08, 08, 04,end_hour = 00, 00, 00,

这几部分,通过sed命令,结合正则表达式,我们可以将上述改为我们需要的日期;

sed -i "s/start_year = \\s*[0-9]\\{4\\}, [0-9]\\{4\\},/start_year = $start_year, $start_year,/" $inputfile
sed -i "s/start_month = \\s*[0-9]\\{2\\}, [0-9]\\{2\\},/start_month = $start_month, $start_month,/" $inputfile
sed -i "s/start_day = \\s*[0-9]\\{2\\}, [0-9]\\{2\\},/start_day = $start_day, $start_day,/" $inputfile
sed -i "s/start_hour = \\s*[0-9]\\{2\\}, [0-9]\\{2\\},/start_hour = $start_hour, $start_hour,/" $inputfile
sed -i "s/end_year = \\s*[0-9]\\{4\\}, [0-9]\\{4\\},/end_year = $end_year, $end_year,/" $inputfile
sed -i "s/end_month = \\s*[0-9]\\{2\\}, [0-9]\\{2\\},/end_month = $end_month, $end_month,/" $inputfile
sed -i "s/end_day = \\s*[0-9]\\{2\\}, [0-9]\\{2\\},/end_day = $end_day, $end_day,/" $inputfile
sed -i "s/end_hour = \\s*[0-9]\\{2\\}, [0-9]\\{2\\},/end_hour = $end_hour, $end_hour,/" $inputfile

通过指定的年月日,进行替换即可。

查询WRF运行状态

wrf在运行时一般通过MPI运行,会生成对应的rsl文件,当成功时,rsl文件会出现:SUCCESSFUL 的字样。
那么通过grep命令,我们可以判断WRF是否完成运行:

#check if the job is runwhile [ `grep SUCCESS rsl.out.0000 |wc -l` -lt 1 ]; docountr=`qstat -r|grep $jobname| wc -l `countq=`qstat -i|grep $jobname| wc -l `count=$(($countr+$countq))until [ $count -ge 1 ];doqsub job.pbscountr=`qstat -r|grep $jobname| wc -l `countq=`qstat -i|grep $jobname| wc -l `count=$(($countr+$countq))echo $countsleep 20tail rsl.out.0000qstat -rdoneecho 'wrf.exe already submited'sleep 30tail rsl.out.0000doneecho "wrf.exe ended: `date`"

日序与日期转换

将日序与日期转换后,再结合定义的模拟日,不断改变start_date:

##function date2julian, julian2date
_date2julian(){d2j_year=$1d2j_month=`expr $2 + 0`d2j_day=`expr $3 + 0`d2j_tmpmonth=$((12*$d2j_year + $d2j_month-3 ))d2j_tmpyear=$(( $d2j_tmpmonth/12 ))_date2juLIAN=$((( 734*$d2j_tmpmonth + 15)/24 - 2*$d2j_tmpyear +$d2j_tmpyear/4 - $d2j_tmpyear/100 + $d2j_tmpyear/400+ $d2j_day + 1721119 ))
}
date2julian()
{
#       echo 'begin d2j'#_date2julian "$1" "$2" "$3" && printf "%d\\n" "$_date2juLIAN"_date2julian "$1" "$2" "$3" && printf "%s\\n" "$_date2juLIAN"
}
#ISO date from JD number
_julian2date()
{j2d_tmpday=$(( $1 - 1721119 ))j2d_centuries=$(( (4 * $j2d_tmpday - 1) / 146097 ))j2d_tmpday=$(( $j2d_tmpday + $j2d_centuries - $j2d_centuries / 4 ))j2d_year=$(( (4 * $j2d_tmpday -1) / 1461))j2d_tmpday=$(( $j2d_tmpday - (1461 * $j2d_year) / 4))j2d_month=$(( (10 * $j2d_tmpday - 5) / 306))j2d_day=$(( $j2d_tmpday - (306 * $j2d_month + 5) / 10))j2d_month=$(( $j2d_month + 2))j2d_year=$(( $j2d_year + $j2d_month / 12))j2d_month=$(( $j2d_month % 12 + 1))
## pad day and month with zeros if necessarycase $j2d_day in ?) j2d_day=0$j2d_day;; esaccase $j2d_month in ?) j2d_month=0$j2d_month;; esac_JULIAN2date=$j2d_year-$j2d_month-$j2d_day
}julian2date()
{#echo 'begin j2d' $1_julian2date "$1" && printf "%s\\n" "$_JULIAN2date"
}
#functiondate2sec(){#convert date to seconds, to be used +-year=$1month=$2day=$3hour=$4datestring=$year-$month-$day" "$hour #¡±:00:00¡±time1=$(date +%s -d "$datestring UTC")echo $time1}      #functionsec2date(){#convert back seconds to datetime2=$1#echo ’t2˙sec2date=’, $titime=$(date +"%Y-%m-%d %H:%M:%S" -u -d "1970-01-01 00:00:00 $time2 seconds")echo $time}

使用

完成了全部的准备工作后,我们需要所有的工作结合,完成整个提交的任务。
为此撰写了3个sh脚本:有日序日期转换的脚本:fuction.date.sh,自动提交WRF任务的auto_wrf.sh,以及循环提交任务,向auto_wrf,sh传递参数的主程序。
再使用时,再主程序脚本中修改对应年月日与模拟天数即可。

#!/bin/bash#for iap server##function date2julian, julian2date
. ./function_date.sh

## main
script_dir=`pwd`
###  user defined parameters:
year1=2020
month1=01
day1=25
hour1=00year2=2020
month2=01
day2=30
hour2=00days_of_simu=1#conver hour to string
chour1=`printf "%02d\\n" $hour1`
chour2=`printf "%02d\\n" $hour2`
#change string to integer, avoid the Octal number:
month1=`expr $month1 + 0 `
day1=`expr $day1 + 0 `
month2=`expr $month2 + 0 `
day2=`expr $day2 + 0 `echo 'simulation start: ',$year1,$month1,$day1,$hour1
echo 'simulation end  : ',$year2,$month2,$day2,$hour2numDay_begin=`date2julian $year1 $month1 $day1`
numDay_end=`date2julian $year2 $month2 $day2`#get the second day, can be used for output dir and decide the season
numDay_day1=($numDay_begin + $days_of_simu)
ymd1_str=`julian2date $numDay_day1`
#year_d1=`echo $ymd1_str|cut -b1-4`
month_d1=`echo $ymd1_str|cut -b6-7`
#output dir
#dir_scheme=${year_d1}${month_d1}
#echo "output dir: " $dir_schemejday_start=$numDay_begin
#Flg_initial=1 #use for geogrid.exe, and spinup files
while [ $jday_start -lt $numDay_end ]
do ymd1_str=`julian2date $jday_start`start_date_str=$ymd1_str" "$hour1":00:00"echo 'this run start:',$start_date_stryearstart=`echo $start_date_str|cut -b1-4`monthstart=`echo $start_date_str|cut -b6-7`daystart=`echo $start_date_str|cut -b9-10`#echo $yearstart, $monthstart, $daystart##the real start and end of this runjday_end=$(($jday_start + $days_of_simu))if [ $jday_end -gt $numDay_end  ];thenjday_end=$numDay_endfiymd2_str=`julian2date $jday_end`end_date_str=$ymd2_str" "$hour2":00:00"echo 'this run end:  ',$end_date_stryearend=`echo $end_date_str|cut -b1-4`monthend=`echo $end_date_str|cut -b6-7`dayend=`echo $end_date_str|cut -b9-10`calculate run_days, run_hourst1=`date +%s -d "${start_date_str}"`t2=`date +%s -d "${end_date_str}"`t1=$((t2-t1))ndays=$((t1/86400))t1=$((t1-ndays*86400))nhours=$((t1/3600))echo "ndays="$ndays,"nhours="$nhours
#iday_out=$(($jday_start + 1))dirout=`julian2date $iday_out`echo 'out_dir=', $dirout#exitstart_year=$yearstartstart_month=$monthstartstart_day=$daystartstart_hour=$chour1end_year=$yearendend_month=$monthendend_day=$dayendend_hour=$chour2echo $start_year,$start_month,$start_day,$start_hour for WPS and PWRFexport start_year start_month start_day start_hourexport end_year   end_month   end_day   end_hourexport Flg_initialcd $script_dirsleep 4s
#if not change schemes accoding season:
#use ERA5+ RDEFT4
#   ./sub_pwrf_ERA5.sh#./sub_wps_pwrf_seaice_era5.sh./auto_wrf.sh
#...............................
#use ERA5#./sub_wps_ERA5.sh#./sub_pwrf_era5.sh
#...............................
exitsleep 5sdateecho "END wrf.exe"if [ $jday_end -eq $numDay_end ];then# has already get the last day, and no need to start another runecho "finished all"#exit, do not use "exit", because another PBLscheme will run#if end hour is 00z, next run starts from yesterday#if end hour is 18z, next run starts from todayif [[ $hour2 -eq "00" || $hour2 -eq "0" ]];thenjday_start=$(($jday_start + $days_of_simu))elsejday_start=$(($jday_start + $days_of_simu ))fifi#then not the firstrunFlg_initial=0doneecho echo "finished "echo 

毒蛇百科