DEXCSランチャー v2.5 製作メモ / 3.6

3.6 メッシュ作成方法(細分化指定有り)の変更(DEXCS化)2



前節までで、細分化用のオプションセットのないメッシュについてはDEXCS方式で作成できるようになった。メッシュ細分化コンテナにおいて将来的には、optimizeLayerオプションや、BoundaryLayerオプションなど追加したいが、それをやる前に、現行の細分化オプションセットだけを使って、DEXCS方式のメッシュ作成ができるようにしておきたい。本節ではその取り組みについて記すが、その際の前提というか、当面の目標について、現行のCfdOFの動作と対比して、どうしたいのかを具体的に掲げておく。

対象例題は、図14に示すように、前節で取り扱った例題に対して、regionという細分化指定領域を追加したモデルを使って実施する。

図14. 対象例題

前節までの手順で作成したメッシュコンテナ(Fusion_Mesh)を選択すると、ツールバーのメッシュ細分化ツールボタンが有効になるので、これをクリックし、図15の手順にて、細分化パラメタを指定していくことになる。

図15. 細分化パラメタの設定方法の例

問題は、ここで択するメニューにおいて、すべてのコンポーネントがリストアップされてしまう事と、設定が完了すると、設定したコンポーネントだけが表示状態で残り、他のコンポーネントがすべて非表示状態になってしまう点である(図16)。

図16. 細分化設定後のモデル表示状態

したがって、当面の目標として、

  • 細分化の対象オブジェクトリストとして、表示オブジェクトだけを対象にする
  • 細分化設定をした後で、既存コンポーネントの表示状態を変更しない。

ようにしておいて、図17に示すように3種類の細分化設定を施したモデルに対して、DEXCS方式のメッシュ作成をする、すなわち前節での改変に加えて、細分化設定条件をこの細分化コンテナから取得して組み込むようにするものである。

図17. 対象モデルの細分化設定

なお、ここに述べた大きな問題以外にも、いくつか改善したい項目もあるが、当面の目標がクリアーできた時点で動作検証し、改めてリストアップする予定である。

また、メッシュ細分化ツールボタンを押した時に起動されるプログラムは、CfdMeshRefinement.py であり、この中で、細分化を指定する為のタスク画面は、

36: import _TaskPanelCfdRefinement
174: taskd = _TaskPanelCfdMeshRefinement._TaskPanelCfdMeshRefinement(self.Object)

として起動されるようになっている。この画面を今後変更していく事になるので、_TaskPanelCfdRefinement.pyをコピーして_dexcsTaskPanelCfdRefinement.py という名前に変更して、以下この内容を変更していく事にする。CfdMeshRefinement.py 本体は、以下変更するだけである。

36: import _dexcsTaskPanelCfdRefinement
174: taskd = _dexcsTaskPanelCfdMeshRefinement._TaskPanelCfdMeshRefinement(self.Object)

この _dexcsTaskPanelCfdRefinement.py の中で、CfdFaceSelectingWidget.py という細分化パーツを選択するツールを起動しており、この選択方法を変更していくので、これもコピーして、dexcsCfdFaceSelectingWidget.py という名前に変更し、この呼び出しに係る部分を、以下変更しておく。

33: import dexcsCfdFaceSelectWidget
51:         self.faceSelector = dexcsCfdFaceSelectWidget.CfdFaceSelectWidget(self.form.referenceSelectWidget,
56:         self.solidSelector = dexcsCfdFaceSelectWidget.CfdFaceSelectWidget(self.form.volReferenceSelectWidget,

先に掲げた問題

  • 細分化の対象オブジェクトリストとして、表示オブジェクトだけを対象にする
  • 細分化設定をした後で、既存コンポーネントの表示状態を変更しない。

については、この_dexcsTaskPanelCfdRefinement.pyの改変にて実現可能であった。つまり、前者の問題については、

108: for i in FreeCADGui.ActiveDocument.Document.Objects:
109:             if "Shape" in i.PropertiesList:
110:                 # Do not restrict to solids
111:                 if not i.Name.startswith("CfdFluidBoundary"):
112:                     self.solidsNames.append(i.Name)
113:                     self.solidsLabels.append(i.Label)

となっていた部分を、以下に変更した。

108: for i in FreeCADGui.ActiveDocument.Document.Objects:
109:             if i.ViewObject.Visibility: 
111:                 if hasattr(i,"Proxy"):
112:                     pass
113:                 else:
114:                     self.solidsNames.append(i.Name)
115:                     self.solidsLabels.append(i.Label)

また、後者の表示状態が変更していたのは、

397:    def closeFaceList(self):
398:         self.form.stackedWidget.setCurrentIndex(0)
399:         self.obj.ViewObject.show()

によるものであった為、399行目をコメントアウトすれば良かった。

以上で、外観上の問題はクリアーできたので、先に進むこととするが、前節において、DEXCSマクロ中の 2 つの関数( makeStlFile と makeMeshDict )を改変したが、これに加えて本節において追加したメッシュ細分化設定項目を反映できるようにしてやれば良さそうである。

(1) 領域細分化指定パーツを除外してSTLファイル作成

makeSrlFileに関しては、細分化領域指定に使用したパーツ(本例ではregion)を除外する必要がある。

そこで、dexcsCfdNeshTools.py の、def makeStlFile(…) において、STL作成対象とするパーツの選択を以下のように変更した。

        outputStlFile = open(self.stlFileName, 'w')

        # refinementRegion として指定(obj.Internal)されたパーツのLabelリストを作成
        __region__=[]
        doc = FreeCAD.activeDocument()
        for obj in doc.Objects:
            if obj.ViewObject.Visibility:
                if hasattr(obj, "Proxy") and isinstance(obj.Proxy, _CfdMeshRefinement):
                    if obj.Internal :                    
                        for objList in(obj.LinkedObjects):
                            __region__.append(objList.Label) 

        for obj in doc.Objects:
            if obj.ViewObject.Visibility:
                __objs__=[]
                try:
                    if obj.Shape:
                        # obj.Labelがregion指定パーツであるかどうか判定
                        checkRegion = False
                        for objRegion in __region__:
                            if obj.Label == objRegion :
                                print('pass ' + obj.Label)
                                checkRegion=True

                        # CfdSolver / Group Object / region指定Obj を除外
                        if obj.isDerivedFrom("Part::FeaturePython") or obj.isDerivedFrom("App::DocumentObjectGroupPython") or checkRegion:
                            pass
                        else:
                            print('append '+obj.Label)
                                
                            __objs__.append(obj)
                            file=self.fileName+obj.Label+'.ast'
                            Mesh.export(__objs__,file)
                            importFile = open(file,'r')
                            temp = importFile.readlines()
                            for line in temp:
                                if 'endsolid' in line:
                                    outputStlFile.write('endsolid ' + obj.Label + '\n')
                                elif 'solid' in line:
                                    outputStlFile.write('solid ' + obj.Label + '\n')
                                else:
                                    outputStlFile.write(line)
                            importFile.close
                            os.remove(file)
                except AttributeError:
                    pass
        outputStlFile.close

朱字部分で、regionの細分化指定対象パーツのリストを取得。青字部分で、対象パーツがこのリストに含まれるかどうか判別するものである。

なお、細分化指定用のパーツ(MeshRefinement…)のプロパティについては、CfdMeshRefinement.py 中、以下のように規定されているので、これを手懸りに設定値を取得できる。

    def initProperties(self, obj):
        # Common to all
        addObjectProperty(obj, "RelativeLength", 0.75, "App::PropertyFloat", "",
                          "Set relative length of the elements for this region")

        addObjectProperty(obj, 'LinkedObjects', [], "App::PropertyLinkList", "", "Linked objects")

        addObjectProperty(obj, "References", [], "App::PropertyPythonObject", "",
                          "List of mesh refinement objects")

        addObjectProperty(obj, "Internal", False, "App::PropertyBool", "",
                          "Whether the refinement region is a volume rather than surface")

        #cfMesh:
        addObjectProperty(obj, "RefinementThickness", "0 m", "App::PropertyLength", "cfMesh",
                          "Set refinement region thickness")

        addObjectProperty(obj, "NumberLayers", 0, "App::PropertyInteger", "cfMesh",
                          "Set number of boundary layers")

        addObjectProperty(obj, "ExpansionRatio", 1.2, "App::PropertyFloat", "cfMesh",
                          "Set expansion ratio within boundary layers")

        addObjectProperty(obj, "FirstLayerHeight", "0 m", "App::PropertyLength", "cfMesh",
                          "Set the maximum first layer height")

        # snappy:
        addObjectProperty(obj, "RegionEdgeRefinement", 1, "App::PropertyFloat", "snappyHexMesh",
                          "Relative edge (feature) refinement")

(2) 領域細分化指定パラメタのmeshDictファイルへの反映

したがって、先に細分化指定したリージョンリスト(__region[]__)を取得したが、これに加えてそれぞれの細分化セルサイズ(RelativeLength)を取得して、以下のように細分化セルサイズリスト(__relativeLength__[])を作成することができる。

        __region__=[]
        __rerativeLength__[]
        doc = FreeCAD.activeDocument()
        for obj in doc.Objects:
            if obj.ViewObject.Visibility:
                if hasattr(obj, "Proxy") and isinstance(obj.Proxy, _CfdMeshRefinement):
                    if obj.Internal :                    
                        for objList in(obj.LinkedObjects):
                            __region__.append(objList.Label) 
                            __relativeLength__.append(obj.RelativeLength) 

オリジナルのDEXCSランチャーにおいて、細分化領域対象パーツを絞り込んでいた部分

         while (self.viewControl.get_gridTableValue(iRow,0)):
             for obj in self.doc.Objects:
                 if obj.Label == self.viewControl.get_gridTableValue(iRow,0):    
                     iRow = iRow + 1
                     if self.viewControl.get_gridTableValue(iRow-1,1) == MainControl.REGION_STR:

を以下のように変更。

        if __region__ :
            regionNumber = 0
            for objList in __region__ :
                for obj in doc.Objects:
                    if obj.Label == objList :

そうすると、対象objの寸法を取得して、BounBox指定の部分は、ほとんどそのまま流用できることになる。ほとんどと記したのは、オリジナルのDEXCSランチャーにおいて、viewControl…としてマクロ画面から取得していたパラメタ部分はMeshRefinement…から取得した値に変更する必要があるという点である。具体的には、

strings7 = [
'\t' + str(self.viewControl.get_gridTableValue(iRow-1,0)) + '\n',
'\t{\n',
'\t\tadditionalRefinementLevels\t' + str(self.viewControl.get_gridTableValue(iRow-1,2)) + ';\n',

青字部分を、以下の赤字部分に変更した。

strings7 = [
'\t' + objList + '\n',
'\t{\n',
'\t\tadditionalRefinementLevels\t' + RefStr + ';\n',

ここに、RefStr(細分化レベル)は暫定的に、

RefStr = str(int( 1.0 / __relativeLength__[regionNumber])-1)

としておくが、この部分はRelativeLengthの指定方法を含めて、改変する予定である。

また、ここで、Box要素でないものを、Box要素相当のものとして設定しているのに対して、今後はBox要素のFreeCADパラメタを直接参照したり、円柱や球などプリミティブ要素を直接的に取り扱うこともできるようにしていく予定である。

(3) レイヤー指定パラメタのmeshDictファイルへの反映

細分化指定したリージョンリストを取得したのと同様に、レイヤー指定したパッチリスト(__patch__)とレイヤー数(__nLayer__)拡大比率(__expRatio__)レイヤー底層厚さ(__firstLayerHeight__)は、以下のようにして取得できる。

        __patch__ = []
        __nLayer__ = []
        __expRatio__ = []
        __firstLayerHeight__ = []
        doc = FreeCAD.activeDocument()
        for obj in doc.Objects:
            if obj.ViewObject.Visibility:
                if hasattr(obj, "Proxy") and isinstance(obj.Proxy, _CfdMeshRefinement):
                    if obj.NumberLayers > 1 :                    
                        for objList in(obj.LinkedObjects):
                            __patch__.append(objList.Label) 
                            __nLayer__.append(obj.NumberLayers) 
                            __expRatio__.append(obj.ExpansionRatio) 
                            __firstLayerHeight__.append(obj.FirstLayerHeight) 

レイヤー指定パッチの絞り込み部分

while (self.viewControl.get_gridTableValue(iRow,0)):
    iRow = iRow + 1
    if self.viewControl.get_gridTableValue(iRow-1,3) == 2:

を以下のように変更

if __patch__ :
    patchNumber = 0
    for objList in __patch__ :
        for obj in doc.Objects:
             if obj.Label == objList :

meshDictに書き込む部分は、以下の青字部分を、

strings3 = [         
'\t\t'                 + str(self.viewControl.get_gridTableValue(iRow-1,0)) + '\n',
'\t\t{\n',
'\t\t\t// number of layers (optional)\n',
'\t\t\tnLayers    '    + str(self.viewControl.get_gridTableValue(iRow-1,4)) + ';\n',
'\t\t\n',
'\t\t\t// thickness ratio (optional)\n',
'\t\t\tthicknessRatio ' + str(self.viewControl.get_gridTableValue(iRow-1,5)) + ';\n',
'\t\t\n',
'\t\t\t// max thickness of the first layer (optional)\n',
'\t\t\t// maxFirstLayerThickness ' + '0.5; // [m]\n',

以下、赤字に変更した。

strings3 = [         
'\t\t'                 + objList + '\n',
'\t\t{\n',
'\t\t\t// number of layers (optional)\n',
'\t\t\tnLayers    '    + str(__nLayer__[patchNumber]) + ';\n',
'\t\t\n',
'\t\t\t// thickness ratio (optional)\n',
'\t\t\tthicknessRatio ' + str(__expRatio__[patchNumber]) + ';\n',
'\t\t\n',
'\t\t\t// max thickness of the first layer (optional)\n',
'\t\t\tmaxFirstLayerThickness ' + str(__firstLayerHeight__[patchNumber]).replace('m','') + '; // [m]\n',

ここで、DEXCSオリジナルのマクロでは、最終行の maxFirstLayerThickness はコメントアウトして出力していたが、これにてパッチ毎の任意の指定が可能になった。

(4) パッチのセルサズ指定パラメタのmeshDictファイルへの反映

セルサイズ指定する部分もほとんど同じである。セルサイズを指定したパッチ名リスト(__patch__)と、相対サイズリスト(__relativeLength__)細分化厚さリスト(__refThickness__)を、以下取得。

        __patch__ = []
        __relativeLength__ = []
        __refThickness__ = []
        doc = FreeCAD.activeDocument()
        for obj in doc.Objects:
            if obj.ViewObject.Visibility:
                if hasattr(obj, "Proxy") and isinstance(obj.Proxy, _CfdMeshRefinement):
                    if (not obj.Internal) and (obj.RelativeLength < 1) :                    
                        for objList in(obj.LinkedObjects):
                            __patch__.append(objList.Label) 
                            __relativeLength__.append(obj.RelativeLength) 
                            __refThickness__.append(obj.RefinementThickness) 

セルサイズ指定パッチの絞り込み部分

while (self.viewControl.get_gridTableValue(iRow,0)):
    iRow = iRow + 1
    if (self.viewControl.get_gridTableValue(iRow-1,2) != Model.EMPTY_STR and 
        self.viewControl.get_gridTableValue(iRow-1,1) != MainControl.REGION_STR):

は、以下変更し、

if __patch__ :
    patchNumber = 0
    for objList in __patch__ :
        for obj in doc.Objects:
             if obj.Label == objList :

さらに、meshDictに書き込む部分は、以下のように暫定変更した。

                RefStr = str(int( 1.0 / __relativeLength__[patchNumber])-1)
                RefThickness = str(__refThickness__[patchNumber]).replace('m','')
                RefThickness = str(float(RefThickness)/1000)

                strings5 = [
                '\t' + objList + '\n',
                '\t{\n',
                '\t\t// additional refinement levels\n',
                '\t\t// to the maxCellSize\n',
                '\t\t additionalRefinementLevels\t' + RefStr + ';\n',
                '\t\n',
                '\t\t// thickness of the refinement region;\n',
                '\t\t// away from the patch;\n',
                '\t\t refinementThickness\t' + RefThickness + ';\n',			
                '\t}\n'
                           ]
                patchNumber = patchNumber + 1
        meshDict.writelines(strings5)

暫定変更というのは、DEXCSオリジナルでは細分化指定の方法を CellSize / RefLevel どちらでも出来るようにしてあったのが、現状のGUIでは設定できないので、RefLevelを使っているが、この値の指定方法もRelativeLengthの値から計算したものになっており、このあたりは今後見直していく予定であるので、暫定処置とした次第である。

前へ 目次 次へ

DEXCSランチャー v2.5 製作メモ / 3.3

3.3 その他の変更



CfdOF をベースに DEXCS ワークベンチ化する際、ソルバー選択等のコンテナは不要になると記した。当
面これを実現する(ツールボタンが出てこないようにする)のに、
InitGui.py 中、以下の 66 〜 69 行を削除

64: cmdlst = [’Cfd_Analysis’,
65:         ’Cfd_MeshFromShape’, ’Cfd_MeshRegion’,
66:         ’Cfd_PhysicsModel’, ’Cfd_FluidMaterial’,
67:         ’Cfd_InitialiseInternal’,
68:         ’Cfd_FluidBoundary’, ’Cfd_InitialisationZone’, 
69:
70:         ’Cfd_SolverControl’]

CfdAnalysis.py 中、以下の 88 〜 98 行は削除(コメントアウト)した。

88: # Add physics object when CfdAnalysis container is created
89: #FreeCADGui.addModule("CfdPhysicsSelection")
90: #FreeCADGui.doCommand("analysis.addObject(CfdPhysicsSelection.makeCfdPhysicsSelection())
91:
92: # Add fluid properties object when CfdAnalysis container is created
93: #FreeCADGui.addModule("CfdFluidMaterial")
94: #FreeCADGui.doCommand("analysis.addObject(CfdFluidMaterial.makeCfdFluidMaterial(’FluidPr
95:
96: # Add initialisation object when CfdAnalysis container is created
97: #FreeCADGui.addModule("CfdInitialiseFlowField")
98: #FreeCADGui.doCommand("analysis.addObject(CfdInitialiseFlowField.makeCfdInitialFlowField
99:
100: # Add solver object when CfdAnalysis container is created
101: FreeCADGui.addModule("CfdSolverFoam")
102: FreeCADGui.doCommand("analysis.addObject(CfdSolverFoam.makeCfdSolverFoam())")

これら以外にも不要なコード、箇所はたくさん存在するが、全体として一通りに動作するようになってから
削除予定。

3.4 CfdOF ⇒dexcsCfdOF 改変の基本方針

メッシュ作成画面を改変するに当たり、ここに改変の基本方針(当面の考え方)を記しておく。すなわち、


CfdOF を改変した dexcsCfdOF ワークベンチから、現行 DEXCS マクロで実装した機能を呼び出せる
ようにして、かつ CfdOF のメッシュ細分化コンテナを拡張し、細分化コンテナから得られるパラメタ
を現行 DEXCS マクロから meshDict ファイルを作成する仕組みの中に組込めるようにする

というものである。

当面と記したのは、 CfdOF のメッシュ作成方法の仕組みが開発元によって改良され、 DEXCS マクロと同
等以上に使えるものになったら置き換えるのも有りだという点と、meshDictファイルを作成するのにTemplateBuilderを使って書き換える方法も有り得るということである。

前へ 目次 次へ

DEXCSランチャー v2.5 製作メモ / 3.5

3.5 メッシュ作成方法(細分化指定無し)の変更(DEXCS化) 1



現行の CfdOF では、メッシュ領域(閉じた領域)を定義した Part オブジェクトを選択しないと、メッシュ
作成画面を表示することが出来ない(ボタンが有効にならない)が、 DEXCS では、 Patr オブジェクトの組み
合わせとしてメッシュ領域を定義するので、何も選択しなくともこの画面が表示できるよう改変したい。
しかし(今の所、その方法がわからないので)、当面は任意のオブジェクトを選択してメッシュ作成画面を

表示できるようにして、この画面とその機能を改変していく事とする。またその際に、メッシュ作成は基本的
に現行の DEXCS 方式を採用するものとして考える。さらに、次節以降のメッシュ細分化パラメタをいかに
組み込むかという課題もあるので、ここではまず細分化パラメタを必要とせず、基本パラメタだけでメッシュ
作成できるケース(backstepSimple)を対象に考えることとした。かように考えるのであれば、図 12 に示すように、

図 12 メッシュ作成画面の改変 1

現行 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 つの関数( makeStlFilemakeMeshDict )が呼び出されており、これら 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 行目の除外方法に至った。

図 13 dexcsCfdOF コンポーネントのプロパティ

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 への対応は将来の課題として、当面はインタフェース部分を削除(もしくは見えないように)する。
  • 改変モジュール中、不要(使わなくなった)部分の削除

前へ 目次 次へ

DEXCSランチャー v2.5 製作メモ / 3.2

3.2 残差プロット 2



ここでは、 DEXCS ランチャーで作成したケースファイルに対して、 [ Analysis control] タスク画面(図 11 )
を改変使用して、計算実行、残差プロット表示できないものか、試みた結果について記しておく。

図 11 [Analysis control] タスク画面

[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 行目が採用される。

ということになる。

前へ 目次 次へ

DEXCSランチャー v2.5 製作メモ/ 3

3 DEXCS ワークベンチ / ランチャー化へ向けての取り組み



まずは、残差プロットの仕組みを、 CfdOF ワークベンチを切り離して使えないものかを調べてみた。メッ
シュ作成からケースセットアップまでは、現行の DEXCS ランチャーで実施できているという前提である。

3.1 残差プロット 1

DEXCS ランチャーにおける[plotWatcher の起動 ] (残差プロット表示)は、 runPlotWatcher.py で、
PyFoam の pyFoamPlotWatcher.py を起動できるようにしたものであった (94 〜 109 行目 ) 。

94: cont = "#!/bin/bash\n"
95: cont = cont + ". " + envOpenFOAMFix + "\n"
96: cont = cont + ’echo -n "running plotWatcher"\n’
97: cont = cont + "pyFoamPlotWatcher.py " + logFile + " 1> /dev/null 2> /dev/null\n"
98: f = open("plotWatcher", "w")
99: f.write(cont)
100: f.close()
101: os.system("chmod a+x plotWatcher")
102: title = solveCaseFix
103: if len(title) > 30:
104: title = "_..." + title[-30:]
105: title = "plotWatcher:" + title
106: #comm = "gnome-terminal --name=" + title + " --geometry=80x2 --hide-menubar -x bash --rcfil
107: comm = "gnome-terminal --name=" + title + " --geometry=80x2 --hide-menubar -- bash --rcfile
108: os.system(comm)
109: 9os.chdir(saveDir)

この部分を以下のように書き換えた。

156:
#print(logFile)
157:
158: UxResiduals = []
159: UyResiduals = []
160: UzResiduals = []
161: pResiduals = []
162: rhoResiduals = []
163: EResiduals = []
164: kResiduals = []
165: omegaResiduals = []
166: niter = 0
167: itimer=1
168:
169: residualPlot = ResidualPlot()
170:
171: #Timer.setInterval(2000)
172: #Timer.timeout.connect(_plotResidual(logFile, niter))
173: #Timer.start()
174: #while itimer:
175: _plotResidual(logFile, niter)
176: #sleep(2)
177: #_plotResidual(logFile, niter)
178: #sleep(2)
179: #_plotResidual(logFile, niter)
180: #sleep(2)

169 行目で、 ResidualPlot() を呼び出しているが、この段階では CfdOF モジュールとのは考えていないので、
便宜的に CfdOF モジュール中の、 CfdResidualPlot.py と同じ内容で、 dexcsCfdResidualPlot.py を用意し
て、 12 行目

from dexcsCfdResidualPlot import ResidualPlot

で使えるようにした。また、 175 行目 plotResidual(logFile, niter) の実体は、

113: def _plotResidual(logFile, niter):
114: print("log=",logFile)
115: f=open(modelDir+"/"+logFile)
116: loglines = f.readlines()
117: #f.close()
118: process_output(loglines, niter)
119: residualPlot.updateResiduals

logFile を読み込んで、その内容を process output(loglines, niter) に渡して、 residualPlot.updateResiduals
(プロット図をアップデート)しようとするもので、 CfdRunnableFoam.py 中のそれを真似して作成したもの
である。 process output(loglines, niter) も、 CfdOF モジュールとの連携を考えていないので、

66: def process_output(text, niter):
67: itimer = 0
68: for line in text:
69: #print(line),
70: split = line.split()
71:
72: # Only store the first residual per timestep
73: if line.startswith(u"Time = "):
74: niter += 1
75:
76: # print split
77: if "Ux," in split and niter-1 > len(UxResiduals):
78: UxResiduals.append(float(split[7].split(’,’)[0]))
79: if "Uy," in split and niter-1 > len(UyResiduals):
80: ... 以下省略 ...

と、 CfdOF モジュールの CfdRunnableFoam.py 中の 102 行目 def process output(self,text) の内容をほと
んどそのまま流用した。 CfdOF オリジナルからの変更点として、引数として niter を加えてあるが、これは
イタレーション回数を見ながら自動更新処理を出来ないかと工夫の名残りである。また、 171 行目〜 180 行目
でコメントアウトした行も、同じ目的で試行錯誤したもので、現時点で自動更新処理は出来ていないので、こ
れらの部分は不要である。
以上の改変にて、残差プロットは CfdOF と同じものを表示できるようになった(見栄えは良くなった!)。
但し、上記説明の中で記したように、計算の開始直後にこれを起動して、計算の進行とともに自動更新処理を
させようと試みたが、実現できていない。
ここで一旦、自動更新の試みを断念して、次節では別のアプローチで残差グラフ表示に取り組むこととする
が、その前に本節の取り組みで判ったことを記しておく。

判明項目17

CfdOF の残差プロットは、計算実行時の標準出力をモニターしているのに対して、今回試みたのは、
実行ログファイルの内容を読み直す方法であり、これでは無理っぽい。

判明項目18


PyFoam の plotWatcher.py はログファイルを監視しつつ更新している(多分、読み直したりはしてい
ない)ので、その方法に習えば、実現できる可能性もあるので、改めて調査予定。

判明項目19

DEXCS ランチャーで、 postProcessing フォルダ中に出力されるデータをプロットするのに、 JGNPlot
を使っているが、 CfdResidualPlot.py を参考に Plot ワークベンチで代用することはできるかもしれな
いので、これも改めて調査予定。

前へ 目次 次へ