由于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 run
while [ `grep SUCCESS rsl.out.0000 |wc -l` -lt 1 ]; do
countr=`qstat -r|grep $jobname| wc -l `
countq=`qstat -i|grep $jobname| wc -l `
count=$(($countr+$countq))
until [ $count -ge 1 ];do
qsub job.pbs
countr=`qstat -r|grep $jobname| wc -l `
countq=`qstat -i|grep $jobname| wc -l `
count=$(($countr+$countq))
echo $count
sleep 20
tail rsl.out.0000
qstat -r
done
echo 'wrf.exe already submited'
sleep 30
tail rsl.out.0000
done
echo "wrf.exe ended: `date`"
日序与日期转换
将日序与日期转换后,再结合定义的模拟日,不断改变start_date:
##function date2julian, julian2date
_date2julian(){
d2j_year=$1
d2j_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 necessary
case $j2d_day in ?) j2d_day=0$j2d_day;; esac
case $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"
}
#################################
#function
date2sec(){
#convert date to seconds, to be used +-
year=$1
month=$2
day=$3
hour=$4
datestring=$year-$month-$day" "$hour #¡±:00:00¡±
time1=$(date +%s -d "$datestring UTC")
echo $time1
}
#function
sec2date(){
#convert back seconds to date
time2=$1
#echo ’t2˙sec2date=’, $ti
time=$(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=00
year2=2020
month2=01
day2=30
hour2=00
days_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,$hour2
numDay_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_scheme
jday_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_str
yearstart=`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 run
jday_end=$(($jday_start + $days_of_simu))
if [ $jday_end -gt $numDay_end ];then
jday_end=$numDay_end
fi
ymd2_str=`julian2date $jday_end`
end_date_str=$ymd2_str" "$hour2":00:00"
echo 'this run end: ',$end_date_str
yearend=`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_hours
t1=`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
#exit
start_year=$yearstart
start_month=$monthstart
start_day=$daystart
start_hour=$chour1
end_year=$yearend
end_month=$monthend
end_day=$dayend
end_hour=$chour2
echo $start_year,$start_month,$start_day,$start_hour
### for WPS and PWRF
export start_year start_month start_day start_hour
export end_year end_month end_day end_hour
export Flg_initial
cd $script_dir
sleep 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
#...............................
exit
sleep 5s
date
echo "END wrf.exe"
if [ $jday_end -eq $numDay_end ];then
# has already get the last day, and no need to start another run
echo "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 today
if [[ $hour2 -eq "00" || $hour2 -eq "0" ]];then
jday_start=$(($jday_start + $days_of_simu))
else
jday_start=$(($jday_start + $days_of_simu ))
fi
fi
#then not the firstrun
Flg_initial=0
done
echo
echo "finished "
echo