3.5 メッシュ作成方法(細分化指定無し)の変更(DEXCS化) 1
現行の CfdOF では、メッシュ領域(閉じた領域)を定義した Part オブジェクトを選択しないと、メッシュ
作成画面を表示することが出来ない(ボタンが有効にならない)が、 DEXCS では、 Patr オブジェクトの組み
合わせとしてメッシュ領域を定義するので、何も選択しなくともこの画面が表示できるよう改変したい。
しかし(今の所、その方法がわからないので)、当面は任意のオブジェクトを選択してメッシュ作成画面を
表示できるようにして、この画面とその機能を改変していく事とする。またその際に、メッシュ作成は基本的
に現行の DEXCS 方式を採用するものとして考える。さらに、次節以降のメッシュ細分化パラメタをいかに
組み込むかという課題もあるので、ここではまず細分化パラメタを必要とせず、基本パラメタだけでメッシュ
作成できるケース(backstepSimple)を対象に考えることとした。かように考えるのであれば、図 12 に示すように、
現行 DEXCS マクロの
- ExportDict ボタン
- MakeMesh ボタン
- ViewMesh ボタン
- maxCellSize の数値カラム
をクリックした際に呼び出される関数を、 CfdOF のメッシュ作成画面上
- Write mesh case ボタン
- Run mesher ボタン
- Paraview ボタン
- Base element size の数値カラム
から呼び出すようにして機能するように改変する事を当面の目標とした。素人プログラマの目論見通りに動いてくれるようになってくれればハッキング与太話も現実的なものになる。逆にこれが出来ない事には、作成状況(実行ログ)を Status カラムに表示させ、中途停止ボタンを機能するようにできるようにする事や、 cfMeshの基本パラメタ( optimize Layer や keepCellsInter… )を追加する事など考えられるが、先へ進むのは尚早である。
(1) dexcs CFD Mesh 画面
_TaskPanelCfdMesh.py をコピーして、_ dexcsTaskPanelCfdMesh.py と名前を変更、この中身を改変して
いくこととした。
また、_TaskPanelCfdMesh.py を呼び出しているのは、 CfdMesh.py からで、この部分を、
163: import _dexcsTaskPanelCfdMesh
164: taskd = _dexcsTaskPanelCfdMesh._TaskPanelCfdMesh(self.Object)
と変更すると共に、メッシュ作成コンテナにおいてメッシュ作成用の CaseName がデフォルトで meshCase
とされているので、これを、
94: addObjectProperty(obj, ’CaseName’, "./", "App::PropertyString", "",
95: "Name of directory in which the mesh is created")
自身の( FreeCAD モデルの存在する)ディレクトリ (.) に変更する。
さらに、タスク画面の GUI デザインはいずれ変更したい。これは TaskPanelCfdMesh.ui にて定義される
ので、これもコピーして名前を変更( dexcsTaskPanelCfdMesh.ui )し、_dexcsTaskPanelCfdMesh.py 中、こ
れを呼び出す部分を以下のように変更した。
51: self.form = FreeCADGui.PySideUic.loadUi(os.path.join(os.path.dirname(__file__), "dexcsTaskPanelCfdMesh.ui"))
(2) _dexcsTaskPanelCfdMesh.py
タスク画面中、 [Write mesh case] ボタンを押した時に実行される関数( writeMesh )は、
def writeMesh(self):
import importlib
importlib.reload(CfdMeshTools)
self.console_message_cart = ’’
self.Start = time.time()
# Re-initialise CfdMeshTools with new parameters
self.store()
FreeCADGui.addModule("CfdMeshTools")
FreeCADGui.addModule("CfdTools")
FreeCADGui.doCommand("FreeCAD.ActiveDocument." + self.mesh_obj.Name + ".Proxy.cart_mesh = "
"CfdMeshTools.CfdMeshTools(FreeCAD.ActiveDocument." + self.mesh_obj.Name + ")")
FreeCADGui.doCommand("cart_mesh = FreeCAD.ActiveDocument." + self.mesh_obj.Name + ".Proxy.c
cart_mesh = self.mesh_obj.Proxy.cart_mesh
self.consoleMessage("Preparing meshing ...")
try:
QApplication.setOverrideCursor(Qt.WaitCursor)
setQuantity(self.form.if_max, str(cart_mesh.getClmax()))
print(’Part to mesh:\n
Name: ’
+ cart_mesh.part_obj.Name + ’, Label: ’
+ cart_mesh.part_obj.Label + ’, ShapeType: ’
+ cart_mesh.part_obj.Shape.ShapeType)
print(’
CharacteristicLengthMax: ’ + str(cart_mesh.clmax))
analysis = CfdTools.getParentAnalysisObject(self.mesh_obj)
FreeCADGui.doCommand("cart_mesh.getFilePaths(CfdTools.getOutputPath(FreeCAD.ActiveD
FreeCADGui.doCommand("cart_mesh.setupMeshCaseDir()")
self.consoleMessage("Exporting mesh refinement data ...")
FreeCADGui.doCommand("cart_mesh.processRefinements()") # Writes stls so need file
FreeCADGui.doCommand("cart_mesh.processDimension()")
FreeCADGui.doCommand("cart_mesh.writeMeshCase()")
self.consoleMessage("Exporting the part surfaces ...")
FreeCADGui.doCommand("cart_mesh.writePartFile()")
self.consoleMessage("Mesh case written to {}".format(self.cart_mesh.meshCaseDir))
except Exception as ex:
self.consoleMessage("Error " + type(ex).__name__ + ": " + str(ex), ’#FF0000’)
raise
finally:
QApplication.restoreOverrideCursor()
self.updateUI()
となっており、2行目の importlib.reload(CfdMeshTools) あたりの役割や、 FreeCADGui.doCommand()などの使い方が今ひとつはっきりしない部分はあったが、わからない部分はそのまま残し、以下のように改変した。
def writeMesh(self):
import importlib
importlib.reload(CfdMeshTools)
self.console_message_cart = ’’
self.Start = time.time()
# Re-initialise CfdMeshTools with new parameters
self.store()
FreeCADGui.addModule("CfdMeshTools")
FreeCADGui.addModule("dexcsCfdMeshTools")
self.consoleMessage("Preparing meshing ...")
cart_mesh = self.cart_mesh
try:
QApplication.setOverrideCursor(Qt.WaitCursor)
FreeCADGui.doCommand("dexcsCfdMesh = dexcsCfdMeshTools.MainControl()")
FreeCADGui.doCommand("dexcsCfdMesh.perform("+ "’" + cart_mesh.meshCaseDir + "’" + ")")
self.consoleMessage("Exporting the part surfaces ...")
except Exception as ex:
self.consoleMessage("Error " + type(ex).__name__ + ": " + str(ex), ’#FF0000’)
raise
finally:
QApplication.restoreOverrideCursor()
self.updateUI()
変更は、
try: 以下のブロック朱字部分で、 DEXCS オリジナルのマクロを改変した dexcsCfdMeshTools.MainControl()
の、 perform を使って、 cfMesh 用のパラメタセットを作成し、かつメッシュ作成用の Allmesh コマンドも生
成するようにした ( マクロ改変の詳細は次節で説明 ) 。
なお、パラメタセットの作成に成功すると、 [Run mesher] ボタンが有効になる。これは self.updateUI() を
実行した際に、 Allmesh の存在が判定理由になっているので、上記 DEXCS マクロの改変の中で、 AllMesh
コマンド作成を追加した次第である。
def updateUI(self):
case_path = self.cart_mesh.meshCaseDir
print(case_path)
self.form.pb_edit_mesh.setEnabled(os.path.exists(case_path))
self.form.pb_run_mesh.setEnabled(os.path.exists(os.path.join(case_path, "Allmesh")))
self.form.pb_paraview.setEnabled(os.path.exists(os.path.join(case_path, "pv.foam")))
self.form.pb_load_mesh.setEnabled(os.path.exists(os.path.join(case_path, "mesh_outside.stl"
utility = self.form.cb_utility.currentText()
if utility == "snappyHexMesh":
self.form.snappySpecificProperties.setVisible(True)
elif utility == "cfMesh":
self.form.snappySpecificProperties.setVisible(False)
Allmesh が所定の形式で作成されていれば、 [Run mesher] ボタンを押して実行される関数( runMesh )
をそのまま改変無しで使ってメッシュ作成ができる。メッシュ作成後の [Paraview] ボタンは下記関数( def
meshFinished )にて有効になるが、これ( openParaview )もそのまま改変無しで使えるようにするには、
pv.foam と pvScriptMesh.py の存在が前提となる。そこで、
def meshFinished(self, exit_code):
if exit_code == 0:
self.consoleMessage(’Meshing completed’)
self.form.pb_run_mesh.setEnabled(True)
self.form.pb_stop_mesh.setEnabled(False)
self.form.pb_paraview.setEnabled(True)
self.form.pb_load_mesh.setEnabled(True)
self.template_path = os.path.join(CfdTools.get_module_path(), "data", "dexcsMesh")
settings={}
settings[’MeshPath’] = self.cart_mesh.meshCaseDir
TemplateBuilder.TemplateBuilder(self.cart_mesh.meshCaseDir, self.template_path, set
上記引用ブロック中の下4行(朱字部)を追加した。これは、.FreeCAD/Mod/CfdOF/data/dexcsMeshフォルダ下に雛形ファイルを収納し、その内容(MeshPathで指定される部分)を書き換えて、所定の場所(self.cart_mesh.meshCaseDir)に収納するものである。
(3) DEXCS マクロの改変 1
FreeCADGui.doCommand("dexcsCfdMesh.perform("+ "’" + cart_mesh.meshCaseDir + "’" + ")")
にて、 DEXCS マクロ( dexcsCfdMeshTools.py ) Maincontrol() クラス中の def perform() が実行される。
その内容は以下の通り(あまり意味のないコメント行やデバッグ用の print 行は削除してある)。
def perform(self, CaseFilePath):
self.makeStlFile(CaseFilePath)
self.fmsFileName = CaseFilePath + MainControl.SLASH_STR + self.fileStem + MainControl.DOT_F
command = ’. ’ + MainControl.BASHRC_PATH_4_OPENFOAM + ";" + MainControl.SURFACE_FEATURE_EDG
command = command + MainControl.SPACE_STR + " 30 "
command = command + MainControl.SPACE_STR + self.stlFileName + MainControl.SPACE_STR
command = command + self.fmsFileName
os.system(command)
constantFolder = CaseFilePath + "/constant"
templateSolver = self.SOLVER_PATH_TEMPLATE
if not os.path.isdir(constantFolder):
command = "cp -r " + templateSolver + "/constant " + CaseFilePath + "/"
os.system(command)
command = "rm -rf " + constantFolder + "/polyMesh"
os.system(command)
systemFolder = CaseFilePath + "/system"
if not os.path.isdir(systemFolder):
command = "cp -r " + templateSolver + "/system " + CaseFilePath + "/"
os.system(command)
zeroFolder = CaseFilePath + "/0"
if not os.path.isdir(zeroFolder):
command = "cp -rf " + templateSolver + "/0 " + CaseFilePath + "/"
os.system(command)
self.makeMeshDict(CaseFilePath)
os.chdir(CaseFilePath)
# cfmeshの実行ファイル作成
caseName = CaseFilePath
title = "#!/bin/bash\n"
envSet = ". " + MainControl.BASHRC_PATH_4_OPENFOAM + ";\n"
solverSet = "cartesianMesh | tee cfmesh.log\n"
sleep = "sleep 2\n"
cont = title + envSet + solverSet + sleep
f=open("./Allmesh","w")
f.write(cont)
f.close()
# 実行権付与
os.system("chmod a+x Allmesh")
最下段の朱字部を追加し、メッシュ作成用のスクリプト Allmesh を作成するようにした。この部分は、オリジナ
ルの DEXCS マクロでメッシュ作成を実行する関数def makeCartesianMeshPerform(self, CaseFilePath):
中にて run スクリプトを作成している部分で、名前を run から Allmesh に変更しただけである。
この perform 関数から、 2 つの関数( makeStlFile と makeMeshDict )が呼び出されており、これら 2 つの
関数の内容も変更する必要があった。
makeStlFile 関数は、読み込んだ CAD ファイルに格納されたデータのうち、 Type が region 以外のオブ
ジェクトを stl ファイルとして出力するもので、 DEXCS マクロのオリジナルでは、 region であるかどうかの
判定に ViewControl クラスを使用していたので、この部分は廃止。替わりに、メッシュ細分化オブジェクト
のプロパティを使用する事になるが、当面は region が無いモデルで実施するので、未組み込みとし、以下の
改変内容にて正常に動作する事を確認した。
1: def makeStlFile(self, CaseFilePath):
2:
3: self.caseFilePath = CaseFilePath
4: self.fileStem = os.path.splitext(self.caseFilePath)[0]
5: self.fileStem = self.fileStem.split(MainControl.SLASH_STR)[-1]
6: self.stlFileName = CaseFilePath + MainControl.SLASH_STR + self.fileStem + ’.stl’
7: self.fileName = CaseFilePath + MainControl.SLASH_STR + self.fileStem
8:
9: outputStlFile = open(self.stlFileName, ’w’)
10:
11: doc = FreeCAD.activeDocument()
12: for obj in doc.Objects:
13: if obj.ViewObject.Visibility:
14: __objs__=[]
15: try:
16: if obj.Shape:
17: # CfdSolver を除外
18: if obj.isDerivedFrom("Part::FeaturePython"):
19: pass
20: else:
21: __objs__.append(obj)
22: file=self.fileName+obj.Label+’.ast’
23: Mesh.export(__objs__,file)
24: importFile = open(file,’r’)
25: temp = importFile.readlines()
26: for line in temp:
27: if ’endsolid’ in line:
28: outputStlFile.write(’endsolid ’ + o
29: elif ’solid’ in line:
30: outputStlFile.write(’solid ’ + obj.
31: else:
32: outputStlFile.write(line)
33: importFile.close
34: os.remove(file)
35: except AttributeError:
36: pass
37: outputStlFile.close
13 行目で obj.ViewObject.Visibility (表示オブジェクト)だけを対象にしている点は従来通りである。ポ
イントは、 16 行目で obj.Shape に絞りこんでいる点であり、これにより、 dexcsCfdOF によって追加される
コンポーネントは、基本的に除外される事になる。但し、 CfdSolver のコンポーネントだけは、何故かプロパ
ティとして Placement を有しており、 obj.Shape として解釈されるようであった(図 13 )。当初これを除外せ
ずとも STL ファイルを作成する際に実害はなかったが、都度エラーメッセージが表示されるので鬱陶しい。
そこで、 18 行目の除外方法に至った。
makeMeshDict 関数は、基本的に cfMesh 用の meshDict ファイルを、マニュアルに記載通りに作成するも
ので、パラメタ変更が必要な部分は viewControl クラスから取得する仕組みとなっていた。そこでこのパラメ
タ変更部分を置き換えれば良いだけのはずである。当面の対象例題( backstepSimple )では、基本パラメタに
係る部分だけを変更対象にするので、パラメタの値を以下のように仮設定しておいて、
testDict_maxCellSize = 0.583
testDict_minCellSize = Model.EMPTY_STR
testDict_untangleLayerCHKOption = 0
testDict_optimiseLayerCHKOption = 0
testDict_keepCellsIntersectingBoundaryCHKOption = 0
testDict_stopAfterEdgeExtractionCHKOption = 0
これを使って、viewControl クラスから値を取得していた部分(例えば、 self.viewControl.get maxCellSizeValue() )
を、以下のように変更した( 6 箇所)。
#’maxCellSize\t’ + str(self.viewControl.get_maxCellSizeValue()) + ’;\n’
’maxCellSize\t’ + str(testDict_maxCellSize) + ’;\n’
さらに、パッチやリージョンをパーツ毎に細分化指定して追加する部分は当面使用しないので、全てコメン
トアウトした。
# local refinement // 指定されており、以外のもの cellSizeregion
# iRow=0
# while (self.viewControl.get_gridTableValue(iRow,0)):
# iRow = iRow + 1
# #print (’### ’, self.viewControl.get_gridTableValue(iRow-1,2) , self.viewControl.get_gridTabl
# if (self.viewControl.get_gridTableValue(iRow-1,2) != Model.EMPTY_STR and
# self.viewControl.get_gridTableValue(iRow-1,1) != MainControl.REGION_STR):
# (以下省略 )
以上の改変にて、当面の対象例題( backstepSimple )にて、 meshDict 作成から⇒メッシュ作成⇒ Paraview
によるメッシュ確認の一連の動作を確認する事が出来た。
(4) メッシュ基本サイズの自動計算と meshDict への反映方法
以上で、当初設定したマイルストーンに到達したことになるが、次のステップ(メッシュ細分化コンテナの
改変)に進む前に、コンテナで設定したパラメタを、従来の viewControl クラスを使わないで、どうやって
meshDict 作成プロセスに反映するかの方法を探った。その最初の手掛かりとして、メッシュ基本サイズにつ
いて、これを反映する方法について調査した。
まずは、メッシュ作成コンテナ中に記された値の取得方法であるが、試行錯誤の末、以下のようにして実現
できた。
#testDict_maxCellSize = 0.583
for obj in FreeCAD.ActiveDocument.Objects:
if hasattr(obj, "Proxy") and isinstance(obj.Proxy, _CfdMesh):
testDict_maxCellSize = obj.CharacteristicLengthMax
また、メッシュ作成コンテナ中の Base element size: の値そのものは、手入力で変更は可能である。し
かし、どうせならモデルを読み込んだ状態にてそのサイズを調べて自動計算した値を表示しておきたい。
CfdMeshTools.py にて、メッシュ作成コンテナが作成される際の def init ブロック中、
# Default to 2 % of bounding box characteristic length
self.clmax = Units.Quantity(self.mesh_obj.CharacteristicLengthMax).Value
if self.clmax <= 0.0:
#shape = self.part_obj.Shape
#cl_bound_mag = math.sqrt(shape.BoundBox.XLength**2 + shape.BoundBox.YLength**2 + shape.Bou
#cl_bound_min = min(min(shape.BoundBox.XLength, shape.BoundBox.YLength), shape.BoundBox.ZLe
#self.clmax = min(0.02*cl_bound_mag, 0.4*cl_bound_min)
xmax = -1.0e+30
xmin = 1.0e+30
ymax = -1.0e+30
ymin = 1.0e+30
zmax = -1.0e+30
zmin = 1.0e+30
doc = FreeCAD.activeDocument()
for obj in doc.Objects:
try:
if obj.Shape:
if obj.Shape.BoundBox.XMax > xmax:
xmax = obj.Shape.BoundBox.XMax
if obj.Shape.BoundBox.XMin < xmin:
xmin = obj.Shape.BoundBox.XMin
if obj.Shape.BoundBox.YMax > ymax:
ymax = obj.Shape.BoundBox.YMax
if obj.Shape.BoundBox.YMin < ymin:
ymin = obj.Shape.BoundBox.YMin
if obj.Shape.BoundBox.ZMax > zmax:
zmax = obj.Shape.BoundBox.ZMax
if obj.Shape.BoundBox.ZMin < zmin:
zmin = obj.Shape.BoundBox.ZMin
except AttributeError:
pass
sumOf3Edges = (xmax-xmin+ymax-ymin+zmax-zmin)
self.mesh_obj.CharacteristicLengthMax = sumOf3Edges / 60.0
と追加することで、変更した値が設定されておればそれをそのまま表示し、それが無い場合( self.clmax=0 )
には、現行 DEXCS マクロと同等の計算方式で算出した値を表示することになる。ちなみにこの最下行ロジックは、
今後改良する予定である。
(5) 残された課題
- makeStlFile 関数において、 region タイプのパーツを除外する方法の組み込み
- 作成されるstl / fmsファイルの名前が、.stl / .fms となっている
- cfMesh の 2D 対応
- スケール変換機能の組み込み
- メッシャーとして、 snappyHexMesh や gmsh への対応は将来の課題として、当面はインタフェース部分を削除(もしくは見えないように)する。
- 改変モジュール中、不要(使わなくなった)部分の削除