こんにちは(@t_kun_kamakiri)
本記事は前回の記事の続編になります。
前回の記事と今回の記事を書いた経緯としてはOpenFOAMのメッシュ作成の際にsnappyHexMeshやcfmeshを使う場合があり、その際にstlの品質が悪い場合にうまくメッシュ生成ができない場合がありました。
特に、cfmeshではstlの品質が問題となりメッシュ生成でエラーが出てしまいます。
そこで、メッシュ品質を直接良くなるかは確認できていませんが、FreeCADでのメッシュ生成方法を検討したので記事としてまとめました。
今回はFreeCADで以下のモデルを作成した状態からはじめます。
前回の記事で書いたFreeCADを使ったstlファイルの自動生成のマクロを使うと下記の一番左のようになります。
FreeCADには「Standard」、「Mefisto」、「Netgen」」、「gmsh」のメッシャーが備わっており四面体であればNetgenを使うことでメッシュ生成が簡単に行えます。
ただし、表面しかメッシュ生成していないのでstlにした際には、今回作成したstlを使ってsnappyHexMeshやcfMeshでメッシュ生成することになります。
では、本記事の内容です。
- FreeCADのNetgenを使ってメッシュ生成
- FreeCADのNetgenの自動化(マクロの使い方)
※今回の記事はこちらのブログのコードを使わせていただいています。
手元のFreeCADでは動作しなかったので編集させていただきました。
FreeCAD 0.20.2
FreeCADのドキュメント
より詳細のFreeCADの使い方に関しては以下のドキュメントを参考にしてください。
Netgenについて
パラメーター | 意味 |
---|---|
Fineness | メッシュの粗密。
から選択できる。 |
Mesh size grading | メッシュサイズの等級。小さいほどメッシュが細かくなります。0.1-1の値。 |
Element per edge | エッジあたりの要素数。大きいほどメッシュが細かくなります。0.1-10の値。 |
Element per curvature radius | 曲率半径あたりの要素数。大きいほどメッシュが細かくなります。0.2-10の値。 |
Optimize surface | 表面形状の最適化を行うかどうか。 |
Second order elements | 2次要素の生成を行うかどうか。 |
Quad dominant | 六面体的なメッシュ配置を行うかどうか。 |
FreeCADでマクロ作成方法
FreeCADにはPythonスクリプトが備わっており、スクリプトを書くことで名前を付けて面分割した状態でメッシュ生成からひとつのファイルにまとめてstlファイルとして出力することができます。
Pythonスクリプトのテンプレートして前回の記事のコードを持ってきます。
前回のコードがこちら。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | import os import Mesh import MeshPart import re doc = App.ActiveDocument PWD = re.findall("(.*)/",doc.FileName)[0] print(PWD) labels = [] doc = App.ActiveDocument print(doc) for obj in doc.Objects: if obj.Visibility == True: mesh = doc.addObject("Mesh::Feature", "Mesh") print(mesh) # mefisto #mesh.Mesh = MeshPart.meshFromShape(Shape=obj.Shape, MaxLength=10) # standard mesh.Mesh = Mesh.Mesh(obj.Shape.tessellate(0.01)) print(mesh.Mesh) label = obj.Label[:] labels.append(label) Mesh.export([mesh], fr'{PWD}/{label}.ast') print(label) doc.removeObject(mesh.Name) print(doc.Label) print(labels, type(labels)) with open(rf'{PWD}/{doc.Label}.stl', 'w') as f: print('test') for label in labels: with open(rf'{PWD}/{label}.ast', 'r') as fi: for line in fi: if line[:5] == 'solid': line = 'solid ' + label + '\n' elif line[:8] == 'endsolid': line = 'endsolid ' + label + '\n' f.write(line) for label in labels: os.remove(rf'{PWD}/{label}.ast') |
こちらを修正していきます。
FreeCADでメッシュ生成のマクロ記録
FreeCAD操作用のライブラリのPythonコードをいきなり書くのはきついので、マクロ記録の起動を使って動作を確認して、コードに埋め込んでいきます。
メッシュデザインワークベンチ「Mesh Design」に移動して以下のようになっている状態とします。
ひとつ面を選択した状態で「メッシュ(M)」>「シェイプからメッシュを生成」をクリックします。
そうすると以下のようにメッシュの生成の設定出てくるので、適当にメッシュを生成します。まずは、デフォルトのまま「OK」をクリックするとPythonコンソールにPythonのコードが記述されます。
これを確認することでメッシュ生成までに必要なコードであることがわかります。
ひとまずこちらはメインのコードにコピペしておきます。(後で編集します)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | for obj in doc.Objects: if obj.Visibility == True: mesh = doc.addObject("Mesh::Feature", "Mesh") print(mesh) # 追加 __doc__=FreeCAD.getDocument("Unnamed") __mesh__=__doc__.addObject("Mesh::Feature","Mesh") __part__=__doc__.getObject("Face") __shape__=Part.getShape(__part__,"") __mesh__.Mesh=MeshPart.meshFromShape(Shape=__shape__, LinearDeflection=0.001, AngularDeflection=0.523599, Relative=True) __mesh__.Label="sidewall1 (Meshed)" del __doc__, __mesh__, __part__, __shape__ # standard コメントアウト #mesh.Mesh = Mesh.Mesh(obj.Shape.tessellate(0.01)) |
こちらを使って編集していきます。
Pythonコードの編集
必要なライブラリを先頭に書きます。
1 2 3 4 | import os import Mesh import MeshPart, Part import re |
現在のディレクトリを以下のように書きます(確認)。
1 2 | # 現在のディレクトリ PWD = re.findall("(.*)/",doc.FileName)[0] |
「doc.FileName」が今開いているFreeCADモデルまでの絶対パスになっています。
※例えば「’D:/OpenFOAM/model/model.FCStd’」のようになっているとしましょう。
re.findall(“(.*)/”,doc.FileName)とすることで”/”より前の文字列を抽出することができきます。re.findall(“(.*)/”,doc.FileName)で[‘D:/OpenFOAM/model/model.FCStd’]のようなリスト型として返ってきますので、リストからインデックス0の要素を抽出すると、現在のディレクトリの文字列を取得できるということです。
これはstlファイルの出力先として指定するときに使います。
今回出力したいstlの保存先を指定するところですね。
1 2 | __doc__=FreeCAD.getDocument("Unnamed") __mesh__=__doc__.addObject("Mesh::Feature","Mesh") |
に関しては、上の
1 2 3 4 5 | doc = App.ActiveDocument 省略 __mesh__=__doc__.addObject("Mesh::Feature","Mesh") |
に相当するので削除します。
1 | __part__=__doc__.getObject("Face") |
は以下のようにします。
1 | __part__= doc.getObject(obj.Name) |
こちらもPythonコンソールで動作を確認すると何が起こっているか確認できます。
面に名前を付けているのですが、デフォルトで以下のように「Face*」となっているのですね。
ですので、Name属性を使って「obj.Name)」としておくと確実です。
1 | __mesh__.Label="sidewall1 (Meshed)" |
は下の
1 | label = obj.Label[:] |
に相当するので削除します。
1 | __mesh__.Mesh=MeshPart.meshFromShape(Shape=__shape__, LinearDeflection=0.001, AngularDeflection=0.523599, Relative=True) |
は、
1 | mesh.Mesh=MeshPart.meshFromShape(Shape=__shape__, LinearDeflection=0.001, AngularDeflection=0.523599, Relative=True) |
のように書き換えます。
1 | del __doc__, __mesh__, __part__, __shape__ |
の中の__doc__はdocに相当して、__mesh__はmeshに相当します。
delをした後も使っているためここでは削除しないようにします。
以下のように変更します。
1 | del __part__, __shape__ |
これで完成です。
全体のコード
全体のコードを少しまとめると以下のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | import os import Mesh import MeshPart, Part # 現在のディレクトリ PWD = r'D:\openfoam\20221019_cfmesh_try\model' print('現在のディレクトリ:',PWD) print(os.getcwd()) labels = [] doc = App.ActiveDocument print(doc) for obj in doc.Objects: if obj.Visibility == True: mesh = doc.addObject("Mesh::Feature", "Mesh") print(mesh) __part__= doc.getObject(obj.Name) __shape__= Part.getShape(__part__,"") ''' # standard mesh.Mesh = Mesh.Mesh(obj.Shape.tessellate(0.01)) mesh.Mesh = MeshPart.meshFromShape(Shape=__shape__, LinearDeflection=0.001, AngularDeflection=0.523599, Relative=True) # netgen(Fineness = 0:非常に粗い, 1:粗い, 2:中程度, 3:細かい, 4:非常に細かい) mesh.Mesh=MeshPart.meshFromShape(Shape=__shape__,Fineness=4,SecondOrder=0,Optimize=1,AllowQuad=0) # netgen user define mesh.Mesh=MeshPart.meshFromShape(Shape=__shape__,GrowthRate=0.1,SegPerEdge=3,SegPerRadius=5,SecondOrder=0,Optimize=1,AllowQuad=0) ''' mesh.Mesh=MeshPart.meshFromShape(Shape=__shape__,GrowthRate=0.1,SegPerEdge=3,SegPerRadius=5,SecondOrder=0,Optimize=1,AllowQuad=0) del __part__, __shape__ label = obj.Label[:] labels.append(label) Mesh.export([mesh], fr'{PWD}/{label}.ast') print(label) doc.removeObject(mesh.Name) print(doc.Label) print(labels, type(labels)) with open(rf'{PWD}/{doc.Label}.stl', 'w') as f: print('test') for label in labels: with open(rf'{PWD}/{label}.ast', 'r') as fi: for line in fi: if line[:5] == 'solid': line = 'solid ' + label + '\n' elif line[:8] == 'endsolid': line = 'endsolid ' + label + '\n' f.write(line) for label in labels: os.remove(rf'{PWD}/{label}.ast') |
メッシュの粗さに応じて、以下の部分を変更してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 | ''' # standard mesh.Mesh = Mesh.Mesh(obj.Shape.tessellate(0.01)) mesh.Mesh = MeshPart.meshFromShape(Shape=__shape__, LinearDeflection=0.001, AngularDeflection=0.523599, Relative=True) # netgen(Fineness = 0:非常に粗い, 1:粗い, 2:中程度, 3:細かい, 4:非常に細かい) mesh.Mesh=MeshPart.meshFromShape(Shape=__shape__,Fineness=4,SecondOrder=0,Optimize=1,AllowQuad=0) # netgen user define mesh.Mesh=MeshPart.meshFromShape(Shape=__shape__,GrowthRate=0.1,SegPerEdge=3,SegPerRadius=5,SecondOrder=0,Optimize=1,AllowQuad=0) ''' mesh.Mesh=MeshPart.meshFromShape(Shape=__shape__,GrowthRate=0.1,SegPerEdge=3,SegPerRadius=5,SecondOrder=0,Optimize=1,AllowQuad=0) |
結果の例を以下に載せておきます。
Netgen(中程度)
Netgen(非常に細かい)
Netgen(ユーザ定義)
今回作成したPythonスクリプトは「ユーザマクロの場所」に保存するとFreeCADから呼び出す子ができます。
本記事のモデルとは異なりますが、マクロを使って面に名前を付けたstlファイルを作成する一連の流れを動画にしています。
ご参考ください。
参考書
本件の内容はFreeCADですが、FreeCADでモデル作成した先にはOpenFOAMで流体解析を行うのでOpenFOAMの参考書を紹介しておきます。
改訂新版 OpenFOAMの歩き方 (技術の泉シリーズ(NextPublishing))