OpenFOAM-tutorials相关脚本解析

$FOAM_TUTORIALS中包含三个脚本:AllrunAllcleanAlltest,以下分别解析。

预备知识

为了看懂以下脚本,首先说明一下bash脚本中经常出现又不太好理解的一些内容。

位置参数

参数 解释
$0 shell脚本的名称
$n 第n个位置参数
$* 含有所有参数内容的单个值,由IFS环境变量中的第一个字母分隔;没有IFS的话,由空格分隔
$@ 将所有命令行参数展开为多个参数
$# 位置参数的综述
$? 上一个命令的退出状态吗
$- 当前选项标记
$$ 当前shell的进程ID(PID
$! 最近一个后台命令的PID

Shell Parameter Expansion

参考gnu官网的bash手册[1]。这里参考另一处给出的例子来说明其含义[2]

用法 说明
${FOO%suffix} 移除短后缀suffix
${FOO#prefix} 移除短前缀prefix
${FOO%%suffix} 移除长后缀suffix
${FOO##prefix} 移除长前缀prefix
${FOO/from/to} 匹配替换
${FOO//from/to} 替换所有
${FOO/%from/to} 后替换
${FOO/#from/to} 前替换

应用举例:

1
2
3
4
5
6
7
8
9
10
11
12
STR="/path/to/foo.cpp"
echo ${STR%.cpp} # /path/to/foo
echo ${STR%.cpp}.o # /path/to/foo.o
echo ${STR%/*} # /path/to

echo ${STR##*.} # cpp (extension)
echo ${STR##*/} # foo.cpp (basepath)

echo ${STR#*/} # path/to/foo.cpp
echo ${STR##*/} # foo.cpp

echo ${STR/foo/bar} # /path/to/bar.cpp

Allrun

简单来说,就是遍历运行目录下的所有算例。其中用到了一个工具foamRunTutorials,其接受两个参数-skipFirst-test,查看帮助给出的说明分别是:do not execute the first Allrun scriptrun test loop。这样也看不出具体是干什么的!直接看foamRunTutorials脚本分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/bin/sh
cd ${0%/*} || exit 1 # Run from this directory
# 其中%/*表示移除最右边的/*,最终${0%/*}表示当前目录

# Source tutorial run functions
. $WM_PROJECT_DIR/bin/tools/RunFunctions # 激活RunFunctions中的函数

# logReport <logfile>
# Extracts useful info from log file
logReport()
{
...
}

# logReportDir <directory>
# Extracts useful info from all log files in a directory
logReportDir()
(
...
)

# Recursively run all cases 遍历地运行所有算例
foamRunTutorials -test -skipFirst

# Analyse all log files 分析所有log文件
rm -f testLoopReport && touch testLoopReport

retVal=0

for appDir in *
do
logReportDir $appDir || retVal=1
done

find . -name "log.*" -exec cat {} \; >> logs

exit $retVal

foamRunTutorials

Description
Run either Allrun or blockMesh/application in current directory and all its subdirectories.

运行当前目录及其子目录下的Allrun脚本或者blockMesh/application。换句话说,如果某个目录下存在Allrun脚本,就执行该脚本;否则就用运行一般算例的方式来执行,即blockMesh生成网格、运行求解器application。使用选项-skipFirst来跳过已存在的Allrun,否则不指定该参数就会直接执行当前目录下的Allrun脚本。-test选项直接传递给Allrun脚本。

个人理解:

  • 这里面使用的make不是用来编译求解器,而是用来递归地切换目录并运行算例
  • FOAM_TARGETS保存了所有一级子目录(使用xargs来分隔结果)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#!/bin/sh
Script=$0
[ "/${Script#/}" != "$Script" ] && Script="$PWD/$Script"

# Normally use standard "make"
make="make"

# Source tutorial run functions
. "$WM_PROJECT_DIR/bin/tools/RunFunctions"

usage() {
cat<<USAGE

Usage: ${0##*/} [OPTIONS]
options:
-help | -h print the usage
-skipFirst | -s do not execute the first Allrun script
-test | -t run test loop

Helper script used by the Allrun scripts in the OpenFOAM tutorials
USAGE
}

skipFirst=false

# Parse options
while [ "$#" -gt 0 ]
do
case "$1" in
-h | -help)
usage && exit 0
;;
-s | -skipFirst)
skipFirst=true
shift
;;
-t | -test)
passArgs="-test"
shift
;;
*)
break
;;
esac
done

# If an argument is supplied do not execute ./Allrun to avoid recursion
if ! $skipFirst && [ -f "./Allrun" ] # 既没有指定skipFirst参数且./Allrun也不存在
then
# Run specialised Allrun script. 运行专门的Allrun脚本
./Allrun $passArgs
elif [ -d system ]
then
# Run normal case. 以普通算例的方式运行
application=$(getApplication)
runApplication blockMesh
runApplication "$application"
else
# Loop over sub-directories and compile any applications
for caseName in *
do
if [ -d "$caseName" -a -d "$caseName/Make" ]
then
( compileApplication "$caseName" )
fi
done
FOAM_TARGETS=$(for d in *; do [ -d "$d" ] && echo "$d"; done | xargs)

# Run all cases which have not already been run
# MakefileDirs是foamRunTutorials专用的Makefile文件
$make -k -f "$WM_PROJECT_DIR/bin/tools/MakefileDirs" \
FOAM_TARGETS="$FOAM_TARGETS" \
FOAM_APP="$Script" FOAM_ARGS="$passArgs"
fi

$WM_PROJECT_DIR/bin/tools/MakefileDirs

1
2
3
4
.PHONY: application $(FOAM_TARGETS)
application: $(FOAM_TARGETS)
$(FOAM_TARGETS):
+@(cd $@ && $(FOAM_APP) $(FOAM_ARGS))

logReport:从log文件中提取有用信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
caseName=`dirname $1 | sed s/"\(.*\)\.\/"/""/g`
app=`echo $1 | sed s/"\(.*\)log\."/""/g`
appAndCase="Application $app - case $caseName"

fatalError=`grep "FOAM FATAL" $1`
UxSS=`grep -E "Ux[:| ]*solution singularity" $1`
UySS=`grep -E "Uy[:| ]*solution singularity" $1`
UzSS=`grep -E "Uz[:| ]*solution singularity" $1`
completed=`grep -E "^[\t ]*[eE]nd" $1`

if [ "$fatalError" ] # 如果有报错
then
echo "$appAndCase: ** FOAM FATAL ERROR **"
return 1
elif [ "$UxSS" -a "$UySS" -a "$UzSS" ]
then
echo "$appAndCase: ** Solution singularity **"
return 1
elif [ "$completed" ]
then
completionTime=`tail -10 $log | grep Execution | cut -d= -f2 | sed 's/^[ \t]*//'`
if [ "$completionTime" ]
then
completionTime="in $completionTime"
fi
echo "$appAndCase: completed $completionTime"
return 0
else
echo "$appAndCase: unconfirmed completion"
return 1
fi

logReportDir:从一个目录中的所有log文件里提取有用信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[ -d $1 ] || return 0

cd $1 || return 1

logs=`find . -name "log.*"`
[ -n "$logs" ] || return 0

retVal=0

for log in `echo $logs | xargs ls -rt`
do
logReport $log >> ../testLoopReport || retVal=1
done
echo "" >> ../testLoopReport

return $retVal

Allclean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/sh
cd ${0%/*} || exit 1 # Run from this directory

dir=${PWD##*/}

echo "--------"
echo "Cleaning ${dir}s ..."
echo "Removing backup files"
find . -type f \( -name "*~" -o -name "*.bak" \) -exec rm {} \;
find . \( -name core -o -name 'core.[1-9]*' \) -exec rm {} \;
find . \( -name '*.pvs' -o -name '*.OpenFOAM' \) -exec rm {} \;
rm logs testLoopReport > /dev/null 2>&1

foamCleanTutorials cases

echo "--------"

Alltest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
cd ${0%/*} || exit 1    # Run from this directory

usage()
{
while [ "$#" -ge 1 ]; do echo "$1"; shift; done
cat<<USAGE

usage: ${0##*/} [OPTION]

options:
-root <dir> specify root directory to run tests from
-default sets up a default scheme on all schemes
-help print the usage

* quickly tests the tutorials and writes out the scheme/solver information

USAGE
exit 1
}

#------------------------------------------------------------------------------

unset DEFAULT_SCHEMES

ROOT="./"

# parse options
while [ "$#" -gt 0 ]
do
case "$1" in
-r | -root)
[ "$#" -ge 2 ] || usage "'$1' option requires an argument"
ROOT="$2"
shift
;;
-h | -help)
usage
;;
-d | -default)
DEFAULT_SCHEMES=true
;;
-*)
usage "unknown option: '$*'"
;;
*)
usage "unknown option/argument: '$*'"
;;
esac
shift
done


setDefaultFvSchemes()
{
cat<<EOF
gradSchemes { default Gauss linear; }
divSchemes
{
default Gauss linear;
div(phi,fu_ft_ha) Gauss multivariateSelection
{
fu upwind;
ft upwind;
h upwind;
};
div(phi,ft_b_ha_hau) Gauss multivariateSelection
{
fu upwind;
ft upwind;
b upwind;
h upwind;
hu upwind;
};
}
laplacianSchemes { default Gauss linear corrected; }
interpolationSchemes { default linear; }
snGradSchemes { default corrected; }
EOF
}

#
# VARIABLE
#
unset MAIN_CONTROL_DICT

for i in \
$HOME/.$WM_PROJECT/$WM_PROJECT_VERSION \
$HOME/.$WM_PROJECT \
$WM_PROJECT_DIR/etc \
;
do
if [ -f "$i/controlDict" ]
then
MAIN_CONTROL_DICT="$i/controlDict"
break
fi
done

[ -f "$MAIN_CONTROL_DICT" ] || usage "main controlDict not found"


TUTORIALS_DIR=$ROOT
TEST_RUN_DIR=../tutorialsTest
FV_SCHEMES=\
" \
gradScheme \
divScheme \
laplacianScheme \
interpolationScheme \
snGradScheme \
"
SCHEMES_FILE="FvSchemes"
SCHEMES_TEMP="FvSchemes.temp"
SOLVERS_FILE="FvSolution"
SOLVERS_TEMP="FvSolution.temp"


#
# MAIN
#

if [ -d "$TEST_RUN_DIR" ]
then
rm -rf $TEST_RUN_DIR
fi

echo "Modifying ${MAIN_CONTROL_DICT}"
if [ -e ${MAIN_CONTROL_DICT}.orig ]
then
echo "File ${MAIN_CONTROL_DICT}.orig already exists"
echo "Did Alltest fail in some way and then run again?"
exit 1
fi

# Clean up on termination and on Ctrl-C
trap 'mv ${MAIN_CONTROL_DICT}.orig ${MAIN_CONTROL_DICT} 2>/dev/null; exit $retVal' \
EXIT TERM INT
cp ${MAIN_CONTROL_DICT} ${MAIN_CONTROL_DICT}.orig

sed \
-e s/"\(fvSchemes[ \t]*\)\([0-9]\);"/"\1 1;"/g \
-e s/"\(solution[ \t]*\)\([0-9]\);"/"\1 1;"/g \
${MAIN_CONTROL_DICT}.orig > ${MAIN_CONTROL_DICT}

echo "Copying the tutorials"
cp -a ${TUTORIALS_DIR} ${TEST_RUN_DIR}

echo "Modifying the controlDicts to run only one time step"
cd ${TEST_RUN_DIR} || exit 1

for CD in `find . -name "controlDict*"`
do
mv ${CD} ${CD}.orig
sed \
-e s/"\(startFrom[ \t]*\)\([a-zA-Z]*\);"/"\1 latestTime;"/g \
-e s/"\(stopAt[ \t]*\)\([a-zA-Z]*\);"/"\1 nextWrite;"/g \
-e s/"\(writeControl[ \t]*\)\([a-zA-Z]*\);"/"\1 timeStep;"/g \
-e s/"\(writeInterval[ \t]*\)\([0-9a-zA-Z.-]*\);"/"\1 1;"/g \
${CD}.orig > ${CD}
done

if [ "$DEFAULT_SCHEMES" = true ]
then
echo "Modifying the fvSchemes to contain only default schemes"
for FV_SC in `find . -name fvSchemes`
do
for S in $FV_SCHEMES
do
mv ${FV_SC} ${FV_SC}.orig
sed -e /"${S}"/,/$p/d ${FV_SC}.orig > ${FV_SC}
done
setDefaultFvSchemes >> ${FV_SC}
done
fi

cp -f $FOAM_TUTORIALS/Allrun .
./Allrun || retVal=1

sed -e :a -e '/\\$/N; s/\\\n//; ta' Allrun > temp
APPLICATIONS=\
`grep "applications=" temp | sed 's/applications=\"\([A-Za-z \t]*\)\"/\1/g'`

rm $SCHEMES_FILE > /dev/null 2>&1
for APP in $APPLICATIONS
do
echo $APP >> $SCHEMES_FILE
echo "$APP: " | tr -d "\n" >> $SOLVERS_FILE
for ST in $FV_SCHEMES
do
rm $SCHEMES_TEMP $SOLVERS_TEMP > /dev/null 2>&1
echo " ${ST}" >> $SCHEMES_FILE
for LOG in `find ${APP} -name "log.${APP}"`
do
for S in `grep ${ST} ${LOG} | cut -d" " -f4`
do
echo " ${S}" >> $SCHEMES_TEMP
done
echo `grep solver ${LOG} | cut -d" " -f4` >> $SOLVERS_TEMP
done
if [ -f $SCHEMES_TEMP ]
then
cat $SCHEMES_TEMP | sort -u >> $SCHEMES_FILE
fi
done
cat $SOLVERS_TEMP | tr " " "\n" | sort -u | tr "\n" " " >> $SOLVERS_FILE
echo "" >> $SOLVERS_FILE
done

exit $retVal


  1. gnu_bash_manual 点击跳转 ↩︎

  2. Bash_scripting_cheatsheet 点击跳转 ↩︎