3.2 残差プロット 2
ここでは、 DEXCS ランチャーで作成したケースファイルに対して、 [ Analysis control] タスク画面(図 11 )
を改変使用して、計算実行、残差プロット表示できないものか、試みた結果について記しておく。
[Analysis control] タスク画面は、 CfdOF ツールバーの [Edit Properties and run solver] ボタンをクリック、または CfdSolver コンテナをダブルクリックした時に現れる。
CfdOF 本来の使用方法としては、 Case[Write] ボタンを押すと解析用のケースフォルダ( case )が作成され、
Solver[Run] ボタンがアクティブになって、これをクリックすると計算実行が始まり、同時に残差プロット画
面が現れ、残差プロット図が 2 秒毎に更新される。 Results[Paraview] ボタンをクリックすれば、 Paraview が
立ち上がって可視化できるという仕組みである。
Solver[Run] ボタンをクリックすると、 _TaskPanelCfdSolverControl.py の 74 行目
74: self.form.pb_run_solver.clicked.connect(self.runSolverProcess)
に従って、 runSolverProcess
152: def runSolverProcess(self):
153: self.Start = time.time()
154:
155: solverDirectory = os.path.join(self.working_dir, self.solver_object.InputCaseName)
156: solverDirectory = os.path.abspath(solverDirectory)
157: cmd = self.solver_runner.get_solver_cmd(solverDirectory)
158: FreeCAD.Console.PrintMessage(’ ’.join(cmd) + ’\n’)
159: envVars = self.solver_runner.getRunEnvironment()
160: QApplication.setOverrideCursor(Qt.WaitCursor)
161: self.solver_run_process.start(cmd, env_vars=envVars)
162: if self.solver_run_process.waitForStarted():
163: # Setting solve button to inactive to ensure that two instances of the same
164: # simultaneously
165: self.form.pb_write_inp.setEnabled(False)
166: self.form.pb_run_solver.setEnabled(False)
167: self.form.terminateSolver.setEnabled(True)
168: self.form.pb_paraview.setEnabled(True)
169: self.consoleMessage("Solver started")
170: else:
171: self.consoleMessage("Error starting solver")
172: QApplication.restoreOverrideCursor()
が起動、 157 行目にて、 self.solver runner.get solver cmd(solverDirectory) を経て、 CfdRunnableFoam.py
の、 get_solver_cmd
89: def get_solver_cmd(self, case_dir):
90: self.initResiduals()
91:
92: self.residualPlot = ResidualPlot()
93:
94: # Environment is sourced in run script, so no need to include in run command
95: cmd = CfdTools.makeRunCommand(’./Allrun’, case_dir, source_env=False)
96: FreeCAD.Console.PrintMessage("Solver run command: " + ’ ’.join(cmd) + "\n")
97: return cmd
となっており、 92 行目で残差プロットを起動、 95 行目で ./Allrun コマンドを実行するというものである。で
は、この Allrun はどうなっているかというと、図 11[Analysis control] タスク画面の Case[Write] ボタンを
クリックした際に自動生成されるもので、以下の内容になっていた。
1: #!/bin/bash
2:
3: runCommand()
4: {
5: if [ "$1" == "mpiexec" ]; then sol="$4"; else sol="$1"; fi
6: sol=$(basename -- "$sol")
7: sol="${sol%.*}"
8: if [ -f log."$sol" ]; then rm log."$sol"; fi
9: "$@" 1> >(tee -a log."$sol") 2> >(tee -a log."$sol" >&2)
10: err1=$?
11: if [ ! $err -eq 0 ]; then exit $err; fi
12: }
13:
14: # Unset and source bashrc
15: FOAMDIR="/home/et/OpenFOAM/OpenFOAM-v2006"
16: source "$FOAMDIR/etc/config.sh/unset" 2> /dev/null
17: source "$FOAMDIR/etc/bashrc"
18:
19: # Copy mesh from mesh case dir if available
20: MESHDIR="../meshCase"
21: if [ -f "$MESHDIR"/constant/polyMesh/faces ]
22: then
23: rm -r constant/polyMesh 2> /dev/null
23: cp -r "$MESHDIR"/constant/polyMesh constant/polyMesh
24: elif [ ! -f constant/polyMesh/faces ]
25: then
26: echo "Fatal error: Unable to find mesh in directory $MESHDIR" 1>\&2
27: exit 1
28: fi
29:
30: # Update patch name and type
31: runCommand createPatch -overwrite
32:
33: # Run application in parallel
34: runCommand decomposePar -force
35: runCommand mpiexec -np 4 simpleFoam -parallel
ポイントは、 19 〜 28 行目にて、メッシュをコピーしている点や、 30 〜 31 行目は createParch にて、境界条
件を設定している点であり、これらは本節での用途では不要となるもので、最終の 2 行で、独自の組み込
み runCommand ( 3 〜 12 行目)で領域分割と並列計算を実行しているということである。そこで、試しに、
DEXCS ランチャーの計算実行時に作成されるスクリプト run を Allrun に変更して使用してみたところ、問
題なく動作する事は確認できた。但し、これだけでは、ケースファイルがデフォルト( /tmp )の case フォル
ダ化にあることが前提なので、「編集」⇒「設定」メニューにて、このデフォルトを、解析モデルが存在する
ディレクトリに設定しておくのと、 CfdSolverFoam.py の 85 行目を以下変更した。
85: #addObjectProperty(obj, "InputCaseName", "case", "App::PropertyFile", "Solver",
86: addObjectProperty(obj, "InputCaseName", "", "App::PropertyFile", "Solver",
そこで、次にこの Allrun を自動作成する方法であるが、これには現行の [Analysis control] タスク画面の
Case[Write] ボタンをクリックした時の動作を変更するというのが順当であろう。つまり、
72:#self.form.pb_write_inp.clicked.connect(self.write_input_file_handler)
73:self.form.pb_write_inp.clicked.connect(self.write_input_file_handler_dexcs)
として 、 write_input_file_handlerの代わりに相 応 の write_input_file_handler_dexcs を作成した 。
write_input_file_handler は、
def write_input_file_handler(self):
self.Start = time.time()
import CfdCaseWriterFoam
import importlib
importlib.reload(CfdCaseWriterFoam)
if self.check_prerequisites_helper():
self.consoleMessage("Case writer called")
self.form.pb_paraview.setEnabled(False)
self.form.pb_edit_inp.setEnabled(False)
self.form.pb_run_solver.setEnabled(False)
QApplication.setOverrideCursor(Qt.WaitCursor)
try:
FreeCADGui.addModule("CfdCaseWriterFoam")
FreeCADGui.doCommand("writer = CfdCaseWriterFoam.CfdCaseWriterFoam(FreeCAD.
self.solver_runner.analysis.Name + ")")
FreeCADGui.doCommand("writer.writeCase()")
except Exception as e:
self.consoleMessage("Error writing case:", "#FF0000")
self.consoleMessage(type(e).__name__ + ": " + str(e), "#FF0000")
self.consoleMessage("Write case setup file failed", "#FF0000")
raise
finally:
QApplication.restoreOverrideCursor()
self.consoleMessage("Write case is completed")
self.updateUI()
self.form.pb_run_solver.setEnabled(True)
else:
self.consoleMessage("Case check failed", "#FF0000")
となっていたのを、以下朱字部分に変更しただけである。
def write_input_file_handler[_dexcs](self):
self.Start = time.time()
import [dexcsCfdCaseWriterFoam]
import importlib
importlib.reload([dexcsCfdCaseWriterFoam])
if self.check_prerequisites_helper():
self.consoleMessage("[Writing dexcs Allrun ...]")
self.form.pb_paraview.setEnabled(False)
self.form.pb_edit_inp.setEnabled(False)
self.form.pb_run_solver.setEnabled(False)
QApplication.setOverrideCursor(Qt.WaitCursor)
try:
FreeCADGui.addModule("[dexcsCfdCaseWriterFoam]")
FreeCADGui.doCommand("writer = [dexcsCfdCaseWriterFoam.dexcsCfdCaseWriteself.solver_runner.analysis.Name + ")")
FreeCADGui.doCommand("writer.[writeAllrun()]")
except Exception as e:
self.consoleMessage("Error writing case:", "#FF0000")
self.consoleMessage(type(e).__name__ + ": " + str(e), "#FF0000")
self.consoleMessage("Write case setup file failed", "#FF0000")
raise
finally:
QApplication.restoreOverrideCursor()
self.consoleMessage("[Write dexcs Allrun is completed]")
self.updateUI()
self.form.pb_run_solver.setEnabled(True)
else:
self.consoleMessage("Case check failed", "#FF0000")
朱字部分の変更も、オリジナルが CfdCaseWriter を import していたのに対し、これを dexcsCfdCaseWriterに変更しているので、関連部を機械的に変更したのと、メッセージの変更、実際の書き込みを行う関数( writer )
の内容が変更するので相応部分を変更しているだけである。
dexcsCfdCaseWriter.py も、オリジナルの CfdCaseWriter.py をベースに変更した。とはいうもの、オリジ
ナルの CfdCaseWriter.py 中で定義している様々な関数は使わない。念の為、
def __init__(self, analysis_obj):
の初期化部分だけはそのまま流用している。これはこの関数がコールされた時の引数( analysis_obj )を
使って、 self.working_dir など、書き込みに必要となるパラメタをここで定義しており、これらはそのまま使
いたいためである *5 。これ以外の関数はすべて削除、新たに Allrun ファイルを作成する関数( writeAllrun )
を、 DEXCS ランチャーの作法に則り作り直している。
55: def writeAllrun(self, progressCallback=None):
56: """ writeCase() will collect case settings, and finally build a runnable case. """
57: cfdMessage("Writing AllrunDexcs to folder {}\n".format(self.working_dir))
58: if not os.path.exists(self.working_dir):
59: raise IOError("Path " + self.working_dir + " does not exist.")
60:
61: # Perform initialisation here rather than __init__ in case of path changes
62: self.case_folder = os.path.join(self.working_dir, self.solver_obj.InputCaseName)
63: self.case_folder = os.path.expanduser(os.path.abspath(self.case_folder))
64:
65: os.chdir(self.case_folder)
66: solver = self.getSolver().replace(’;’,’’)
67:
68: import pathlib
69: fname = os.path.join(self.case_folder, "Allrun")
70: p_new = pathlib.Path(fname)
71:
72: title = "#!/bin/bash\n"
73: configDict = pyDexcsSwakSubset.readConfigTreeFoam()
74: envOpenFOAMFix = configDict["bashrcFOAM"]
75: configDict = pyDexcsSwakSubset.readConfigDexcs()
76: envTreeFoam = configDict["TreeFoam"]
77: envOpenFOAMFix = envOpenFOAMFix.replace(’ $ TreeFoamUserPath’,envTreeFoam)
78: envOpenFOAMFix = os.path.expanduser(envOpenFOAMFix)
79: envSet = "source " + envOpenFOAMFix + ’\n’
80: #envSet = ". /opt/OpenFOAM/OpenFOAM-v2006/etc/bashrc\n"
81: solverSet = solver + " | tee solve.log"
82: cont = title + envSet + solverSet
83:
84: with p_new.open(mode=’w’) as f:
85: f.write(cont)
86:
87: # Update Allrun permission - will fail silently on Windows
88: fname = os.path.join(self.case_folder, "Allrun")
89: import stat
90: s = os.stat(fname)
91: os.chmod(fname, s.st_mode | stat.S_IEXEC)
92:
93: cfdMessage("Successfully wrote case to folder {}\n".format(self.working_dir))
94:
95: self.template_path = os.path.join(CfdTools.get_module_path(), "data", "dexcs")
96: settings={}
97: settings[’system’]={}
98: settings[’system’][’CasePath’] = self.case_folder
99: TemplateBuilder.TemplateBuilder(self.case_folder, self.template_path, settings)
100:
101: return True
55 〜 63 、 87 〜 93 行は、オリジナルの writeCase 関数部分から流用しており、 72 〜 85 が、 DEXCS ランチャーと同じやり方で実行スクリプトを作成している部分である。ここで 66 行目でソルバー名( solver )を、 getSolver 関数で取得している。留意点として、ソルバー名を取得する関数として、オリジナルの CfdCaseWriterFoam.pyでは getSolverName という関数を定義しているが、これは CfdOF のコンテナ情報から取得するもので、DEXCS ランチャーではケースファイルが存在するという前提なので、 system/controlDict を読み取って取得( getSolver )という違いがある。
また、 95 〜 99 行目は、 Paraview の起動用に、スクリプト( pvScript.py )と、ダミーファイル pv.foam を作成するもので、本来この関数( writeAllrun )に含めるべき内容ではないが、便宜的にここで作成してみたものであり、いずれは別関数なりで処理することにしたい。本ブロックは、先の 2.4 にて、テンプレートファイルを書き換える仕組みとして TemplateBuilder について汎用的に使えると記したが、ここで実際にそういう使い方を実践してみたということであり、本例を参考にすれば、応用範囲も拡げられそうである。
以下簡単に、本例での書き換え内容を説明しておく。 99 行目が、
TemplateBuilder.TemplateBuilder(self.case_folder, self.template_path, settings)
と な っ て お り 、こ れ で 、「 self.template path 中 の フ ァ イ ル を settings に 応 じ て 書 き 換 え た も の
を self.case folder に 収 納 す る 」と い う こ と を や っ て く れ る 。こ こ で 、 self.template path は こ の 場
合、 .FreeCAD/Mod/CfdOF/data/dexcs であり、その中には、 pv.Foam と、 pvScriopt.py という 2 つの
ファイルを収納してある。 pv.foam はダミーファイルで単にそのまま self.case folder (解析用のケースフォルダ)にコピーされるだけであるが、 pvScriopt.py は、以下のようになっており、
1: #### import the simple module from the paraview
2: from paraview.simple import *
3: #### disable automatic camera reset on ’Show’
4: paraview.simple._DisableFirstRenderCameraReset()
5:
6: # create a new OpenFOAMReader
7: pfoam = OpenFOAMReader(FileName=r’%(system/CasePath%)/pv.foam’)
8: %{%(solver/Parallel%)
9: %:True
10: pfoam.CaseType = ’Decomposed Case’
11: %:False
12: pfoam.CaseType = ’Reconstructed Case’
13: %}
14: pfoam.Decomposepolyhedra = 0
15:
16: # get active view
17: renderView1 = GetActive
18: ...... 以下省略
これを setting パラメタ(辞書)を使って書き換えるということである。本例で定義した setting は 1 項目
settings[’system’][’CasePath’] = self.case_folder
だけなので、
- 7 行目の、 %(system/CasePath%) の部分を、 self.case folder に置き換える。
- 8 行目の %(solver/Parallel%) に対しては、この辞書が存在しないので、 12 行目が採用される。
ということになる。