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

3-7.中間まとめ



前節までの取り組みで、CfdOFのGUIを使って、DEXCSランチャーの一部(ソルバー実行と残渣グラフ表示)とDEXCSマクロの主要プログラムを起動できるようになったはずである。そこでDEXCSの標準チュートリアルケースを題材に、一連の動作確認を実施し、今後に必要な改善項目を取り纏めた。

(1) FreeCADモデル

モデルは任意のフォルダに収納しておく。

図18. 解析フォルダ

これをダブルクリックすれば、普通にCADモデルが表示される。

図19. DEXCS標準チュートリアル問題

(2) dexcsCfdOFワークベンチ

ワークベンチを切り替える

図20. dexcsCfdOFワークベンチ

ツールバーが現れるので、[CfdAnalysis]ボタンをクリック。

図21. CfdAnalysis解析コンテナ

コンボビューのモデルツリー下部に、[dexcsCfdAnalysis]と、その下に階層を下げて[CfdSolver]というコンテナが作成される。

ここで、一旦、「編集」→「設定」メニューを起動して、

図22. Default output directory の設定

Default output directory が、モデル収納フォルダになるようにしておく。他のフォルダでも実行は可能だが、これがDEXCS流。

(3)メッシュ作成コンテナ

設定を終わったら、前の画面に戻り、表示パーツ(のどれでも良い)を選択すると、

図23.メッシュ作成コンテナ

dexcsCfdOFツールバー中の[メッシュ作成]ボタンが有効になるので、これをクリック。

図24.メッシュ作成タスク画面

タスク画面が現れるが、最下段にBase element size として 0.27 の数字があり、これはDEXCS方式で計算した推奨パラメタである。

このモデルでは、このままでは適切なメッシュとならないので、一旦この画面は「閉じる」

図25.メッシュ作成コンテナ

モデルツリー上、最下段にBlock_meshというコンテナができているが、この名前は[メッシュ作成]ボタンを押した時に選択対象としたパーツによって変わってくる。今の所、気にしなくてよい。

(4)メッシュ細分化コンテナ

メッシュ作成[block_Mesh]コンテナを選択すると、[メッシュ細分化]ボタンが有効になるので、これをクリックすると、

図26.メッシュ細分化タスク画面

Mesh refinement のタスク画面が開くので、この画面を使って細分化の詳細を設定する。

図27.細分化対象パーツの選択(1/2)

細分化対象としたいパーツは、[select from list]ボタンをクリックすると、[Select Object]の選択リストが有効になるので、このリストの中から選択する。[Dexcs]を選択すると、少し待ち時間があって後、長大なFaceリストが現れる。これはDexcsを構成する全Faceを示しており、これを取得する為時間がかかっているのである。それぞれのFace…の名前の先頭にチェックボックスが表示されており、これの選択如何で、細分化対象をさらに詳細に設定することができるようになっているというのがCfdOF本来の使い方であるが、DEXCSではそういうことはしない。したがって、ここでFaceリストを表示させるのでなく、単に選択パーツを表示させたかったのであるが、その為の工夫が容易ではなさそうであったので、当面はCfdOF本来の選択表示方法を流用していくこととした。

図28.細分化対象パーツの選択(2/2)

ここで、[Select all]ボタンを押せば、全Faceにチェックマークが付くので、ここで[Done]ボタンを押す。そうすると、Dexcs:Face…というリスト表示が得られるので、選択はこれで完了である。

レイヤーを作成したい場合には[Boundary Layer]のチェックボックスにチェックを入れると、レイヤーの詳細設定メニューが現れるので、

図29.パッチ毎の細分化設定1

レイヤー数が2以上になるように変更すれが、これが有効になる。設定が完了したら[OK]ボタンをクリック。なお、以上の説明では、Dexcsの構成Faceを全選択したが、どれか一つのFaceを選択しただけであっても良い(下図)。CfdOF本来のメッシュ作成法ではDEXCSフォントのほんの一部分しか細分化されない事になるが、中身はDEXCS方式でメッシュ作成しており、このリスト中のパーツ名(Dexcs)を使っているにすぎないからである。

図30.パッチ毎の細分化設定2(略式)

Dexcsフォントに対する細分化が終わったら、改めて[Block_mesh]コンポーネントを選択して、ボタンをクリック。今度は、regionBoxに対する細分化設定を行う。こちらは内部領域の細分化であるので[internal Volume]にチェックする。

図31.領域内部の細分化設定

パーツの選択方法として、[Select from list]ボタンを押してリストから選択することはSurface(パッチ)の場合と同じであるが、inletやoutletを選択してもSolidではないので、選択欄にリストアップされない。間違い防止にはなっている。

[Relative element size:]の値は任意に変更して良いが、一応この値(0.3)でRefLevel=2相当になる。

(5)メッシュ作成

細分化設定が終わったら、改めて[Block_Mesh]のメッシュ作成コンテナをダブルクリックして、メッシュ作成のタスク画面を開き、[Write mesh case]ボタンを押す。設定に問題が無ければ、[Run mesher]ボタンが有効になるので、これをクリックしてメッシュ作成が実行される。[Paraview]ボタンでメッシュ確認、問題なければ[閉じる]ボタンをクリック。

Paraviewによる確認においては、起動画面において既にモデルがロードされた状態で表示される(図32)。

図32.Paraviewの起動画面

Decompose polyhedra のチェックボタンも非有効となっているので、メッシュ確認までの手間が省けている。

(6)計算実行

メッシュが完成したら、[CfdSolver]コンテナをダブルクリック。[Analysis control]タスク画面が開くので、[Write]ボタン→[Run]ボタンクリックにて計算が始まり、残渣プロットが表示される。

図33.計算実行状況

計算終了後のParaview確認においても、メッシュ確認の場合と同様、モデルがロードされた状態で起動する(Applyボタンを押さなくともよい)ので、手間が省けている。

(7)まとめ

  • dexcsCfdOF(CfdOFの名前を変更し不要機能を除外したもの)のGUIを使って、DEXCSマクロによるDEXCS式メッシュ作成を実現し、DEXCSランチャーの一部(ソルバー実行と残渣グラフ表示)を代替することができた。
  • 現状では、Default output directoryをケース変更の都度変更する必要があり、これはモデルの存在するディレクトリがデフォルトになるよう変更したい。→ 解決1
  • メッシュ作成コンテナを作成するのに、3Dパーツを選択する必要があり、選択したパーツによって、コンテナの名前が変わってしまう。→3Dパーツを選択せずとも、[CfdAnalysis]ボタンを押した時に、[CfdSolver]コンテナと同時に作成されるようにしたい。→ 解決2
  • メッシュ作成の際に生成される形状ファイルの名前が.stl, .fmsになってしまっている(meshDict中の名前もそうなっているので、メッシュ生成には支障ない)が、DEXCS標準(FreeCADモデル名と同じ)としたい。→ 解決3
  • 現行DEXCSマクロで設定出来ているパラメタセットと比べて、不足・問題になるものは以下の通り。
    • featureAngle
    • optimise_Layer
    • keepCellsIntersectingBoundary
    • stopAfterEdgeExtraction
    • 細分化方法の CellSize / RefLevel  切り替え
    • RefLevel をRelative element size より算出している点
  • 一方、現行DEXCSマクロで設定出来ていない以下の機能を組み込むことができた。
    • Refinement thickness
    • Max first cell height

解決1

CfdTools.py中、 def getDefaultPath()

def getDefaultOutputPath():
    prefs = getPreferencesLocation()
    output_path = FreeCAD.ParamGet(prefs).GetString("DefaultOutputPath", "")
    if not output_path:
        #output_path = tempfile.gettempdir()
        output_path = os.path.dirname(FreeCAD.ActiveDocument.FileName) 
    output_path = os.path.normpath(output_path)
    return output_path

ただし、モデルが変更になった時には、先のモデル情報が残ってしまうので、起動時にはその設定を読み込まないようにした。CfdPreferencePage.py中、 def __init__(self) セクション中、最後に以下の2行を追加した。

        prefs = CfdTools.getPreferencesLocation()
        FreeCAD.ParamGet(prefs).SetString("DefaultOutputPath", "")

通常はFreeCADモデルが存在するディレクトリがケースファイルとされる。何らかの事情によって、メッシュを特別なディレクトリに出力したい場合には、編集→設定により、出力先を変更できるが、この設定はその場限りで有効になるという使い方を想定している。

DEXCS方式では、ケースフォルダ中に隠しファイルを収納し、その有無と内容如何で出力先を変更しているので、その方式に変更する可能性は残してあるのと、dexcsCfdAnalysisコンテナの [Output Path]プロパティを直接編集して出力先を変更することができて、この場合はここに指定した出力先が優先されるという点もあるので、もう少しわかりやすくしたほうがよいかも。

また、出力先を変更した場合に、DEXCSランチャーのGridEditor等の起動ボタンが使えなくなる(ケース情報が伝わらない)点もあり、今後の課題となる。

解決2

[CfdAnalysis]ボタンを押した時に、コンテナが作成されるのは、CfdAnalysis.py 中の、def Activated(sel) セクションであるので、この中に、以下の2行を追加した。

        # Add mesh object when CfdAnalysis container is created
        FreeCADGui.addModule("CfdMesh")
        FreeCADGui.doCommand("analysis.addObject(CfdMesh.makeCfdMesh())")

但し、コンテナは作成されるものの、このままでは、これをダブルクリックして設定画面を開こうとすると、エラーになった。これはこのコンテナのプロパティを初期化する部分(CfdMesh.py 中の、def initProperties(self, obj):)において、メッシュ作成用の CaseName を以下のように

__caseName__ = ""
addObjectProperty(obj, 'CaseName', __caseName__, "App::PropertyString", "",
                          "Name of directory in which the mesh is created")

再変更する必要があった。これは、上記の解決1によって、解析ケース名を変更しているので、それに応じた対応ということである。

また、CfdMeshTools.py の、def getFilePaths(self,output_dir) において、else: 以下の最終行でCfdOFオリジナルのSTLファイルを作成するためのファイル名を作成しているが、このままだと参照エラー等生じてしまうので、ダミーのファイル名(geometry)を割り当てた。

        else:
            #self.temp_file_geo = os.path.join(self.constantDir, 'triSurface', self.part_obj.Name + '_Geometry.stl')
            self.temp_file_geo = 'geometry.stl'

解決3

stlファイルの名前を作成しているのは、 dexcsCfdMeshTools.py 中、def makeStlfile(…)

self.caseFilePath = CaseFilePath
self.fileStem = os.path.splitext(self.caseFilePath)[0]
self.fileStem = self.fileStem.split(MainControl.SLASH_STR)[-1]
self.stlFileName = CaseFilePath + MainControl.SLASH_STR + self.fileStem + '.stl'
self.fileName = CaseFilePath + MainControl.SLASH_STR + self.fileStem

としていた部分を、

modelName = os.path.splitext(os.path.basename(FreeCAD.ActiveDocument.FileName))[0]
self.stlFileName = CaseFilePath + modelName + '.stl'
self.fileName = CaseFilePath

と変更。

同様に、fmsファイルの名前は、def perform(…)において

self.fmsFileName = CaseFilePath + MainControl.SLASH_STR + self.fileStem + MainControl.DOT_FMS_STR

としていた部分を、

modelName = os.path.splitext(os.path.basename(FreeCAD.ActiveDocument.FileName))[0]
self.fmsFileName = CaseFilePath + modelName + ".fms"

と変更することにより実現できた。

前へ 目次 次へ

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

ということになる。

前へ 目次 次へ