はじめに
以前の投稿で、空力プロセスをモデリングするプログラムでサードパーティのオープンソースライブラリFreefem ++およびNetGenを使用する機能を検討しました。 ライセンスの観点からこれらのライブラリを商用プロジェクトに含める可能性、機能を実行する機能、およびソフトウェアアーキテクチャに含めることについてでした。 この記事は前の記事を補足するもので、NetGenライブラリをさらに詳しく調べます。 興味深いのは、モデルの表面上の特定の領域で有限要素の3Dメッシュを生成する機能です。
MS Visual StudioのNetGen接続
NetGenをC ++プログラムに接続する方法を示します。 NetGenプロジェクトの公式サイトからアーカイブをダウンロードします。 このケースでは、NetGenバージョン4.9.13が利用可能でした。 ライブラリを接続するには、アーカイブから3つのフォルダーlibsrc、nglib、windowsが必要です。 Visual Studioで、デモプログラムのプロジェクトとソリューションを作成し、windowsフォルダーから既存のプロジェクトファイルnglib.vcxprojを添付します。 この実験では、nglibプロジェクトはVisual Studioの以前のバージョンで作成されたため、プロジェクトを新しいバージョンに同時に変換します。 nglibプロジェクトの設定で、libsrc \ includeフォルダーを追加し、NO_PARALLEL_THREADSシンボルを追加し、追加のリンカー依存関係からpthreadVC2.libを削除し、リンカーポストイベントを削除し、エラーなしでプロジェクトがビルドされるようにターゲットフォルダーを構成します。
nglib.hヘッダーファイルのインクルードは、あまり馴染みのない次の方法で記述する必要があります。
名前空間nglib { #include "nglib.h" } 名前空間nglibを使用します。
テストモデル
有限要素で埋める必要があるスペースは、NetGenで2つの方法で指定できます。
- 建設的ブロックジオメトリ(CSG)演算子の形式。
- STLファイル内のスペース境界の記述形式。
ここでは、表面記述の2番目のバリアントであるSTLテキスト形式の3次元モデルの記述のみを検討します。 試験モデルのグラフィック画像を図に示します。 1.モデルは、サイズ10 X 10 X 5の平行六面体です。面の1つに、オフセット(4; 2)のあるサイズ5 X 2の長方形領域があり、その境界は変更されないままでなければなりません。

図 1. STLファイルを形成するための表面の三角形分割。 境界線を変更しない領域は赤で強調表示されます
以下は、テストモデルを説明するSTLファイルの内容です。
固い ファセット法線1 0 0外側のループ頂点10 4 2頂点10 6.5 3頂点10 4 4エンドループエンドファセット ファセット法線1 0 0外側ループ頂点10 6.5 3頂点10 9 4頂点10 4 4エンドループエンドファセット ファセット法線1 0 0外側ループ頂点10 9 2頂点10 6.5 3頂点10 4 2エンドループエンドファセット ファセット法線1 0 0外側のループ頂点10 9 2頂点10 9 4頂点10 6.5 3エンドループエンドファセット ファセット法線1 0 0外側のループ頂点10 0 5頂点10 0 0頂点10 4 2エンドループエンドファセット ファセット法線1 0 0外側のループ頂点10 4 2頂点10 4 4頂点10 0 5エンドループエンドファセット ファセット法線1 0 0外側のループ頂点10 10 5頂点10 0 5頂点10 4 4エンドループエンドファセット ファセット法線1 0 0外側のループ頂点10 9 4頂点10 10 5頂点10 4 4エンドループエンドファセット ファセット法線1 0 0外側のループ頂点10 9 2頂点10 10 5頂点10 9 4エンドループエンドファセット ファセット法線1 0 0外側のループ頂点10 10 0頂点10 10 5頂点10 9 2エンドループエンドファセット ファセット法線1 0 0外側ループ頂点10 10 0頂点10 9 2頂点10 4 2エンドループエンドファセット ファセット法線1 0 0外側のループ頂点10 10 0頂点10 4 2頂点10 0 0 endloop endfacet ファセット法線-1 0 0外側のループ頂点0 10 5頂点0 10 0頂点0 0 5エンドループエンドファセット ファセット法線-1 0 0外側のループ頂点0 0 0頂点0 0 5頂点0 10 0 endloop endfacet ファセット法線0 -1 0外側のループ頂点0 0 5頂点0 0 0頂点10 0 5エンドループエンドファセット ファセット法線0 -1 0外側のループ頂点10 0 0頂点10 0 5頂点0 0 0 endloop endfacet ファセット法線0 1 0外側ループ頂点10 10 5頂点10 10 0頂点0 10 5エンドループエンドファセット ファセット法線0 1 0外側のループ頂点0 10 0頂点0 10 5頂点10 10 0 endloop endfacet ファセット法線0 0 1外側のループ頂点0 0 5頂点10 0 5頂点0 10 5エンドループエンドファセット ファセット法線0 0 1外側のループ頂点10 10 5頂点0 10 5頂点10 0 5エンドループエンドファセット ファセット法線0 0 -1外側のループ頂点10 0 0頂点0 0 0頂点10 10 0 endloop endfacet ファセット法線0 0 -1外側のループ頂点0 10 0頂点10 10 0頂点0 0 0 endloop endfacet エンドソリッド
STLファイルの情報の形式は次のとおりです。 ファイルの最初と最後に、キーワードsolidとendsolidが示されており、その間に、フォーマットで必要なサーフェスを指定する三角形がリストされています。
ファセット法線nx ny nz 外側のループ 頂点v1x v1y v1z 頂点v2x v2y v2z 頂点v3x v3y v3z エンドループ 端面ここで、nx、ny、nzは軸X、Y、Zに沿った法線ベクトルの成分で、シミュレートされたオブジェクトから外側に向けられています。 (v1x、v1y、v1z)、(v2x、v2y、v2z)、(v3x、v3y、v3z)-三角形の3つの頂点の座標。
有限要素メッシュ生成プログラムの例
以下は、有限要素メッシュを取得するために必要なすべてのステップを実行するプログラムのテキストです。 このプログラムはOneLegionBar.stl STLファイルを読み取り、有限要素グリッドをOneRegionBar.volファイルに書き込みます。
#include "stdafx.h" #include <iostream> #include <fstream> 名前空間nglib { #include ".. \ NetGen \ nglib \ nglib.h" } 名前空間stdを使用します。 名前空間nglibを使用します。 void main() { const char * stlFileName = ".. \\ Data \\ OneRegionBar.stl"; const char * volFileName = ".. \\ Data \\ OneRegionBar.vol"; //表面のリブ const int EDGES_NUM = 4; const int POINTS_NUM = 4; typedef double Point [3]; ポイントポイント[POINTS_NUM] = {{10、4、2}、{10、9、2}、{10、9、4}、{10、4、4}}; typedefペア<int、int> Edge; エッジエッジ[EDGES_NUM] = {エッジ(0,1)、エッジ(1、2)、エッジ(2,3)、エッジ(3、0)}; //メッシュ生成のパラメーター Ng_Meshing_Parameters ngMeshParameters; ngMeshParameters.maxh = 10; ngMeshParameters.fineness = 0.4; ngMeshParameters.secondorder = 0; ngMeshParameters.quad_dominated = 0; ngMeshParameters.grading = 0.0; ngMeshParameters.optsurfmeshenable = false; ngMeshParameters.optsteps_2d = 0; ngMeshParameters.closeedgeenable = false; //操作の結果 Ng_Result ng_res; // //メッシュ作成を開始 // // STLファイルを読み取り、「STLジオメトリ」オブジェクトを作成します Ng_STL_Geometry * stlGeometry = Ng_STL_LoadGeometry(stlFileName); //不変のエッジを「ジオメトリ」に追加します for(int i = 0; i <EDGES_NUM; i ++) { double * p1 =ポイント[エッジ[i] .first]; double * p2 =ポイント[エッジ[i] .second]; Ng_STL_AddEdge(stlGeometry、p1、p2); } // STL記述でオブジェクトを初期化します ng_res = Ng_STL_InitSTLGeometry(stlGeometry); if(ng_res!= NG_OK) { cout << "Ng_STL_InitSTLGeometryのエラー" << endl; } //現在空のアイテムグリッド Ng_Mesh * mesh = Ng_NewMesh(); //モデルサーフェスにエッジを形成します ng_res = Ng_STL_MakeEdges(stlGeometry、mesh、およびngMeshParameters); if(ng_res!= NG_OK) { cout << "Ng_STL_MakeEdgesのエラー" << endl; } //表面空間を生成します ng_res = Ng_STL_GenerateSurfaceMesh(stlGeometry、mesh、およびngMeshParameters); if(ng_res!= NG_OK) { cout << "Ng_STL_GenerateSurfaceMeshのエラー" << endl; } //有限要素ネットワークを生成します ng_res = Ng_GenerateVolumeMesh(mesh、&ngMeshParameters); if(ng_res!= NG_OK) { cout << "Ng_GenerateVolumeMeshのエラー" << endl; } //有限要素のメッシュをファイルに保存します Ng_SaveMesh(mesh、volFileName); }
次に、プログラムテキストに関する詳細なコメントを示します。
- エッジのエッジであるポイントの座標は、ポイント[POINTS_NUM]配列にあります。 エッジ[EDGES_NUM]配列は、対応するポイント番号のペアの形で表面領域のエッジを定義します。 これらのエッジは、モデルサーフェスで保持されるようにNetGenで指定する必要があります。
- グリッドを生成するプロセスは、Ng_Meshing_ParametersクラスのngMeshParametersオブジェクトで収集されたパラメーターによって制御されます。 パラメーターの1つ-maxhは、グリッド要素の最大サイズを制限します。 他のパラメーターを使用して、生成プロセスを微調整できます:要素の最小サイズの制限、グリッド最適化の開始など。
- STLファイルはNg_STL_LoadGeometry()関数によって読み取られ、STLジオメトリオブジェクトが返されます。
Ng_STL_Geometry * stlGeometry = Ng_STL_LoadGeometry(stlFileName);
Ng_STL_Geometryは、STLジオメトリを持つオブジェクトへのポインタのタイプとして理解する必要があります。 実際、Ng_STL_Geometryは、他のいくつかのタイプと同様に、void *と同義です。 この方法でNetGenの作成者は、複雑なオブジェクトの実装機能を完全に隠しました。
- 次に、STLジオメトリオブジェクトにエッジを追加します。 これらは、Ng_STL_AddEdge()関数を使用してループに追加されます。
Ng_STL_AddEdge(stlGeometry、p1、p2);
stlGeometryはSTLジオメトリのオブジェクトです。 p1とp2は、エッジの頂点です。 各頂点は、X、Y、Z軸に沿ったコンポーネントの配列によって定義され、これらの頂点は、以前に形成されたエッジエッジの配列から選択されます。
- Ng_STL_InitSTLGeometry()関数を呼び出して、STLジオメトリオブジェクトを初期化します。
ng_res = Ng_STL_InitSTLGeometry(stlGeometry);
- メッシュが指す空の要素メッシュオブジェクトを作成します。 これを行うには、Ng_NewMesh()関数が実行されます。
Ng_Mesh * mesh = Ng_NewMesh();
- STLジオメトリと生成パラメーターに従って、モデルサーフェスにエッジを形成します。
ng_res = Ng_STL_MakeEdges(stlGeometry、mesh、およびngMeshParameters);
- Ng_STL_GenerateSurfaceMesh()関数を使用して、モデルサーフェスを三角形化します。 ここでは、以前に定義された歪みのないエッジがすでに考慮されており、生成パラメーターが考慮されます。
ng_res = Ng_STL_GenerateSurfaceMesh(stlGeometry、mesh、およびngMeshParameters);
- そして最後に、生成グリッドを考慮してNg_GenerateVolumeMesh()関数によって要素グリッドが直接生成されます。
ng_res = Ng_GenerateVolumeMesh(mesh、&ngMeshParameters);
- このプログラムでは、グリッド生成結果がNg_SaveMesh()関数によってVOLファイルに出力されます。これは、グリッドへのポインターとファイル名を転送します。
Ng_SaveMesh(mesh、volFileName);
グリッド生成結果
メッシュ生成の結果は、Ng_Mesh型のポインターによるプログラムオブジェクトです。デモプログラムでは、メッシュポインターです。 要素のグリッドのプロパティへのプログラムによるアクセスを提供する関数のセットがあります:グリッド内のポイントの数Ng_GetNP()、表面上の三角形の数Ng_GetNSE()、有限要素の数Ng_GetNE()を取得、点の座標Ng_GetPoint()、表面要素Ng_GetSurfaceElement( )、最終要素Ng_GetVolumeElement()を取得し、グリッドをNg_SaveMeshファイルに保存します。
プログラムに保存されたVOLファイルは、netgen.exeビジュアライザーで開くことができます。 ファイルを表示した結果を図5に示します。 2.図からわかるように、表面領域の境界は変化しませんでした。

図 2.要素のメッシュを生成した後のモデルの表面
NetGenによって生成されるVOLファイルの形式は次のとおりです。
- ファイルの先頭には、フォーマットコード(mesh3d)、モデルの次元(次元3)、およびジオメトリコード(geomtype 0)が示されています。
mesh3d 寸法 3 geomtype 0
- 次に、キーワードsurfaceelementsの後に、数量が示され、表面要素がリストされます。 各要素について、表面の数、表面の「材料」の数、予約された整数フィールド、表面要素を記述する点の数(三角形-3の場合)、および表面要素の頂点の3点の数が記録されます。 以下、ポイントには1から番号が付けられます。
#surfnr bcnr domin domout np p1 p2 p3 表面要素 546 2 1 1 0 3 3 4 13 2 1 1 0 3 4 5107 #...
- ファイルには、「材料」の数が記録される有限要素、1つの要素の頂点の数(四面体の場合は4つあります)、および頂点である点の数がリストされます。
#matnr np p1 p2 p3 p4 ボリューム要素 1009 1 4 213 85153214 1 4 298 307 301 305 #...
- 表面のエッジに関する情報が表示されます:
#surfid 0 p1 p2 trignum1 trignum2 domin / surfnr1 domout / surfnr2 ednr1 dist1 ednr2 dist2 edgesegmentsgi2 220 1 0 1 2 1 1 0 0 1 0 1 2 2 0 2 1 6 6 0 0 1 2 1 0 #...
- グリッドのノードであるポイントがリストされます:
#XYZ ポイント 333 10.0000000000000000 4.0000000000000000 4.0000000000000000 10.0000000000000000 4.0000000000000000 2.0000000000000000 #...
- ファイルの最後には、数字で識別される表面の色成分が書き込まれています。 これらの色は、表面をペイントするためにビジュアライザーによって使用されます。 ここでは、番号2のサーフェスの場合、図の長方形の領域を強調表示するように色が設定されます。 2。
#Surfnrレッドグリーンブルー face_colours 7 2 1.00000000 1.00000000 0.00000000 3 0.00000000 1.00000000 0.00000000 #...
NetGenを使用した2Dメッシュ
NetGenには、要素の2Dメッシュを構築する機能があります。 特定の場合、この関数は、たとえば、平坦な面を持つ3DモデルのSTLファイルを生成する場合に役立ちます。 モデルのジオメトリは、空間の領域の境界として入力ファイルに記述されます。 パーティションのサブドメインの境界は、ラインセグメントまたは2次スプラインを使用して指定されます。 境界線を記述するときは、境界線の左右の領域の番号を示します。 グリッドの外側の領域は番号0でエンコードされます。

図 3.要素の2Dメッシュを生成するためのポイントとエリアのコーディング
図に対応する入力ファイルの例を示します。 3。
splinecurves2dv2 2 ポイント 1 0 0 2 3 0 3 3 2 4 0 2 5 1 0 6 2 0 7 2 1 8 1 1 セグメント 1 0 2 1 5 -bc = 1 2 0 2 5 6 -bc = 1 1 0 2 6 2 -bc = 1 1 0 2 2 3 -bc = 1 1 0 2 3 4 -bc = 1 1 0 2 4 1 -bc = 1 2 1 2 6 7 -bc = 1 2 1 2 7 8 -bc = 1 2 1 2 8 5 -bc = 1 材料 1ドメイン1 -maxh = 1 2 domain2 -maxh = 1
splinecurves2dv2キーワードは、このファイルの先頭に示されています。 それに続くのは、グリッドの再構築係数です(ここでは-2)。 キーワードポイントの後に、モデルの説明ポイントがリストされます:ポイント番号とX、Y軸に沿った座標。
次に、キーワードセグメントの後に、境界セグメントのリストが次の形式で続きます。
- 境界線の左側のエリア番号。
- 境界線の右側のエリア番号。
- 境界セグメントを記述するポイントの数(例2)。
- 境界線の開始点の番号。
- 境界のエンドポイント番号。
- 生成制御フラグ。 bcフラグ(境界条件番号)は、使用されていない場合でも指定する必要があります。
キーワード材料の後に、エリア(「材料」)が次の形式でリストされます。
- エリア番号。
- マテリアルの名前。
- 生成制御フラグ。 ここでmaxhフラグを指定して、要素グリッドの最大サイズを制限できます。
以下は、2Dメッシュを生成するプログラムです。
#include "stdafx.h" 名前空間nglib { #include ".. \ NetGen \ nglib \ nglib.h" } 名前空間nglibを使用します。 void main(){ const char * in2DFileName = ".. \\ Data \\ triangulation.in2d"; const char * volFileName = ".. \\ Data \\ triangulation.vol"; Ng_Geometry_2D * geom; Ng_Init(); geom = Ng_LoadGeometry_2D(in2DFileName); Ng_Meshing_Parameters mp; mp.maxh = 10000; mp.fineness = 1; mp.secondorder = 0; Ng_Mesh * mesh; Ng_GenerateMesh_2D(geom、&mesh、&mp); Ng_SaveMesh(mesh、volFileName); }
2Dメッシュを生成するには、geomポインターを使用した2Dジオメトリ記述オブジェクトが使用されます。 メッシュ生成の結果は、メッシュポインターによってオブジェクト内にあります。 この例では、3Dメッシュ生成の例と同じNg_SaveMesh()関数を使用して生成結果を表示しています。 したがって、この場合、出力ファイルには3Dメッシュの構造がありますが、その中のZ座標の値は常にゼロです。 2Dグリッドを操作するためのプログラムインターフェイスには、関数Ng_GetNP_2D()-グリッドノードの数を取得、Ng_GetNE_2D()-グリッド内の要素数を取得、Ng_GetPoint_2D()-ノード番号によるグリッドノード座標の取得、Ng_GetElement_2D()-要素の頂点の座標を取得アイテム番号。 2Dメッシュの生成結果の視覚化を図に示します。 4.図からわかるように、領域の境界は歪んでいません。

図 4. 2Dメッシュを生成した結果
おわりに
この記事では、NetGenの概要を簡単に説明します。そのため、このライブラリは、要素の3Dメッシュの生成を必要とするアプリケーションで便利に使用できます。 ライブラリ関数は、コードを記述せずに学習して使用できます。 NetGenには、すべてのライブラリ関数への視覚的なインターフェイスを実装するアプリケーションnetgen.exeが含まれています。 要素のグリッドを描いたこの記事の図面は、このアプリケーションを使用して取得されます。