'22/05/21更新:雛形コードを可読性のために若干見直し
本記事では、CADファイルの汎用形式(.step)をPythonライブラリの「Gmsh」を用いてメッシュ分割する雛形コードを載せました。出力するメッシュ形式は、Gmshフォーマット(.msh)と市販のFEMソフトAbaqusフォーマット(.inp)です。WindowsとLinux(Ubuntu)環境下で動作確認しています。
■ライブラリのインストール方法
◆pipの場合
pip uninstall gmsh-dev gmsh-sdk gmsh-sdk-git
pip install --upgrade gmsh
下記はその参照リンク先です。
◆Anaconda環境下の場合
conda config --add channels conda-forge
conda install gmsh python-gmsh
下記はその参照リンク先です。
■以降で、本プログラムの実施例を示します
▼ CADファイル(.step)を準備する
下図は、CADソフト(オープンソースのFreeCAD)で作成した3次元構造の例です。4つの領域ソリッドで構成されたひとつのcomponent solidです。これを拡張子.stepのCADファイルで出力します。
▼ Gmshによるメッシュ分割
下図は、本プログラムを実行して、CADファイル(.step)からメッシュファイル(.inp, .msh)を生成した2つの例です。この2つは、メッシュ設定が異なります。
■本プログラム
#!/usr/bin/python3 # -*- coding: utf-8 -*- import os, sys, glob import shutil import gmsh import time ##### パラメータ ここから # CADファイル(.step)のあるフォルダをリストで抽出 cadfile_list = glob.glob(r'./CAD_stepfiles/*.step') # 出力先フォルダ' out_dirpath = './FEM_inpfiles' # メッシュ条件を辞書で設定(keyがファイル名になる) my_mesh_method_dict = { # 1. 辺当たりの節点数を指定したメッシュ #'transfinite':(20,), # 2. 段階メッシュ 'gradual_1':(0.01, 3), 'gradual_2':(0.01, 10), # 3. 一様メッシュ #'uniform':(5,) } # 要素タイプを2次にする場合:True しない場合:False is_secondary_element = False # 領域間の重複する線や面を削除する場合:True しない場合:False is_fragment = True # 出来栄えをgmsh GUIを立ち上げて目視確認する(複数ある場合は順番に) is_check_gmsh_gui = True ##### パラメータ ここまで measure_time_list = [] # 初期化 ''' if os.path.isdir(out_dirpath): # 保損先フォルダがある場合 shutil.rmtree(out_dirpath) # 一旦中身ごと削除 time.sleep(1) os.mkdir(out_dirpath) # 保存先フォルダ作成 ''' # 上記で定義したメッシュ条件の辞書をループして、メッシュ生成する for my_mesh_method_key, my_meth_method_value in my_mesh_method_dict.items(): for cadfile in cadfile_list: # ファイルパスからファイル名の取得 file_name = os.path.basename(cadfile) # ファイル名から、拡張子なしのファイル名と拡張子の取得 file_name, extension = os.path.splitext(file_name) print("processing " + file_name, my_mesh_method_key) start = time.time() ##### 前処理 ##### # オブジェクトの生成 gmsh.initialize() # 進行状況を表示する場合:1 # Default value: 0 gmsh.option.setNumber("General.Terminal", 1) # ファイル開く gmsh.model.occ.importShapes(cadfile) gmsh.model.occ.synchronize() ents = gmsh.model.getEntities() #print("entities", ents) # フラグメント調整。領域間の界面(面や辺)を連続にする。重複ある場合は取り除く if is_fragment: try: ent3D = gmsh.model.getEntities(3) gmsh.model.occ.fragment(ent3D[:1], ent3D[1:], removeObject=True, removeTool=True) gmsh.model.occ.synchronize() gmsh.model.setVisibility(ents, True, recursive = True) #領域名を再定義 for k, dimtag in enumerate(gmsh.model.getEntities(3)): value = gmsh.model.addPhysicalGroup(3, [dimtag[1]], tag=dimtag[1]) gmsh.model.setPhysicalName(3, value, 'volume'+str(value)) for k, dimtag in enumerate(gmsh.model.getEntities(2)): value = gmsh.model.addPhysicalGroup(2, [dimtag[1]], tag=dimtag[1]) gmsh.model.setPhysicalName(2, value, 'surface'+str(value)) except ValueError as e: # 領域がひとつのみの構造の場合は次の処理へ print(e) ##### メッシュ分割 ##### # 3Dメッシュアルゴリズム # (1: Delaunay, 3: Initial mesh only, 4: Frontal, 7: MMG3D, 9: R-tree, 10: HXT) # Default value: 1 gmsh.option.setNumber("Mesh.Algorithm3D", 1) # 1. 辺当たりの節点数を指定したメッシュ if 'transfinite' in my_mesh_method_key: NN = my_meth_method_value[0] for c in gmsh.model.getEntities(1): gmsh.model.mesh.setTransfiniteCurve(c[1], NN) for s in gmsh.model.getEntities(2): gmsh.model.mesh.setTransfiniteSurface(s[1]) # 四辺形要素にするか。Falseの場合に三角形 is_rectangle = False if is_rectangle: gmsh.model.mesh.setRecombine(s[0], s[1]) gmsh.model.mesh.setSmoothing(s[0], s[1], 100) for v in gmsh.model.getEntities(3): gmsh.option.setNumber("Mesh.Recombine3DLevel", 2) gmsh.option.setNumber("Mesh.Recombine3DConformity", 1) gmsh.model.mesh.setTransfiniteVolume(v[1]) # 2. 段階メッシュ elif 'gradual' in my_mesh_method_key: element_size_min = my_meth_method_value[0] element_size_max = my_meth_method_value[1] options = dict() options['Mesh.CharacteristicLengthMin'] = element_size_min options['Mesh.CharacteristicLengthMax'] = element_size_max #options['Mesh.CharacteristicLengthFromPoints'] = 0 #options['Mesh.Algorithm'] = 8 #options['Mesh.CharacteristicLengthExtendFromBoundary'] = 1 #options['Mesh.Smoothing'] = 5 #options['Mesh.RecombinationAlgorithm'] = 0 options['Mesh.RecombineAll'] = 0 # 4面体の場合0, 6面体の場合1 #options['Geometry.Tolerance'] = 0.01 for mo, val in iter(options.items()): gmsh.option.setNumber(mo, val) # 3. 一様メッシュ elif 'uniform' in my_mesh_method_key: element_size = my_meth_method_value[0] ent = gmsh.model.getEntities(0) #print('entity', ent) gmsh.model.mesh.setSize(ent, element_size) # メッシュ分割 #gmsh.model.mesh.generate(1) # 1次元メッシュ #gmsh.model.mesh.generate(2) # 2次元メッシュ gmsh.model.mesh.generate(3) # 3次元メッシュ # 2次要素にする場合 if is_secondary_element: gmsh.model.mesh.setOrder(2) ###### 後処理 ##### # Gmshに関するメッシュフォーマット gmsh.option.setNumber("Mesh.MshFileVersion", 2.2) # 設定した値をファイル名に使用するために、タプルをアンパック代入で取得 a, *b = my_meth_method_value if b: value_str = str(a) + '_' + str(b[0]) else: value_str = str(a) out_file_path = os.path.join(out_dirpath, file_name + '_' + 'gmsh_' + my_mesh_method_key + '_' + value_str) # gmshフォーマットで保存 save_file_path = out_file_path + '.msh' gmsh.write(save_file_path) # Abaqusフォーマットで保存 save_file_path = out_file_path + '.inp' gmsh.write(save_file_path) # 出来栄えをgmsh GUIを立ち上げて目視確認する if '-nopopup' not in sys.argv and is_check_gmsh_gui: gmsh.fltk.run() # オブジェクトを閉じる gmsh.finalize() # 処理時間をリストへ格納 elapsed_time = time.time() - start out_file_name = os.path.basename(out_file_path) measure_time_list.append((out_file_name, elapsed_time)) # 処理に要した時間をcsvファイルに出力する with open('processing_time_of_gmsh.csv', 'w', encoding='utf-8') as f: for i, mydata in enumerate(measure_time_list, start=1): if i == 1: f.write(r'No,Name,Time' + '\n') buf = ','.join([str(i), mydata[0], str(round(mydata[1], 1))]) f.write(buf + '\n') print('gmsh finished')
Gmshのメッシュオプションは多々あります。しかし、構造によっては、メッシュ生成に失敗することも少なくありません。
参考までに、「Gmsh」と同様のライブラリ「Netgen」の場合は今のところ百発百中でメッシュできます。リンクは下記です。
(その他参考URL)
以上
<広告>
リンク
リンク
リンク
リンク