1726 lines
72 KiB
C#
1726 lines
72 KiB
C#
|
|
using CaeGlobals;
|
|||
|
|
using CaeMesh.Meshing;
|
|||
|
|
using GmshCommon;
|
|||
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.IO;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Threading;
|
|||
|
|
using System.Windows.Forms;
|
|||
|
|
// ReSharper disable SuggestVarOrType_Elsewhere
|
|||
|
|
// ReSharper disable SuggestVarOrType_BuiltInTypes
|
|||
|
|
#pragma warning disable IDE0130
|
|||
|
|
|
|||
|
|
namespace CaeMesh
|
|||
|
|
{
|
|||
|
|
public class GmshAPI
|
|||
|
|
{
|
|||
|
|
// Variables
|
|||
|
|
private GmshData _gmshData;
|
|||
|
|
private Action<string> _writeOutput;
|
|||
|
|
private bool _isOCC;
|
|||
|
|
private string _error;
|
|||
|
|
private Thread _thread;
|
|||
|
|
private int _currentLogLine;
|
|||
|
|
|
|||
|
|
|
|||
|
|
// Properties
|
|||
|
|
public GmshData GmshData => _gmshData;
|
|||
|
|
|
|||
|
|
// 构造函数
|
|||
|
|
public GmshAPI(GmshData gmshData, Action<string> writeOutput)
|
|||
|
|
{
|
|||
|
|
_gmshData = gmshData;
|
|||
|
|
_writeOutput = writeOutput;
|
|||
|
|
|
|||
|
|
if (_gmshData.MeshFileName != null)
|
|||
|
|
{
|
|||
|
|
_isOCC = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (_gmshData.GeometryFileName != null)
|
|||
|
|
{
|
|||
|
|
if (_gmshData.GeometryFileName.EndsWith("brep"))
|
|||
|
|
{
|
|||
|
|
_isOCC = true;
|
|||
|
|
}
|
|||
|
|
else if (_gmshData.GeometryFileName.EndsWith("stl"))
|
|||
|
|
{
|
|||
|
|
_isOCC = false;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
throw new NotSupportedException();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 五种公开接口
|
|||
|
|
public string CreateMesh()
|
|||
|
|
{
|
|||
|
|
return RunInBackground(CreateMeshBackground);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public string Defeature()
|
|||
|
|
{
|
|||
|
|
return RunInBackground(DefeatureBackground);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public string GetOccNormals()
|
|||
|
|
{
|
|||
|
|
return RunInBackground(GetOccNormalsBackground);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public string GetElementQualities()
|
|||
|
|
{
|
|||
|
|
return RunInBackground(GetElementQualitiesBackground);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public string GetCoordinatesFromParameterization()
|
|||
|
|
{
|
|||
|
|
return RunInBackground(GetCoordinatesFromParameterizationBackground);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 后台运行
|
|||
|
|
private string RunInBackground(Action action)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
Gmsh.Initialize();
|
|||
|
|
Gmsh.Model.Add("Model-1");
|
|||
|
|
Gmsh.Logger.Start();
|
|||
|
|
_currentLogLine = 0;
|
|||
|
|
|
|||
|
|
// Common options
|
|||
|
|
Gmsh.Option.SetNumber("Geometry.OCCScaling", 1);
|
|||
|
|
// by Luke 2025-9-6 未来将两个参数加入设置中
|
|||
|
|
Gmsh.Option.SetNumber("Geometry.OCCFixDegenerated", 1);
|
|||
|
|
// Gmsh.Option.SetNumber("OCCFixSmallFaces", 1);
|
|||
|
|
|
|||
|
|
// 工作线程
|
|||
|
|
_thread = new Thread(new ThreadStart(() => action()));
|
|||
|
|
_thread.Start();
|
|||
|
|
|
|||
|
|
// Running
|
|||
|
|
int count = 0;
|
|||
|
|
while (_thread.ThreadState == ThreadState.Running)
|
|||
|
|
{
|
|||
|
|
if (count++ % 10 == 0) WriteLog();
|
|||
|
|
Application.DoEvents();
|
|||
|
|
Thread.Sleep(100);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Wait for the worker thread to finish
|
|||
|
|
_thread.Join();
|
|||
|
|
|
|||
|
|
//
|
|||
|
|
WriteLog();
|
|||
|
|
//
|
|||
|
|
return _error;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_error = ex.Message;
|
|||
|
|
_writeOutput?.Invoke(_error);
|
|||
|
|
_thread?.Abort();
|
|||
|
|
//
|
|||
|
|
return _error;
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
Gmsh.Logger.Stop();
|
|||
|
|
Gmsh.FinalizeAll();
|
|||
|
|
|
|||
|
|
// Check if the thread was not aborted on error
|
|||
|
|
if (_error != null)
|
|||
|
|
{
|
|||
|
|
File.Delete(_gmshData.InpFileName);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Meshing
|
|||
|
|
private void CreateMeshBackground()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (_gmshData.GmshSetupItems.Length != 1)
|
|||
|
|
{
|
|||
|
|
throw new CaeException("Currently, for a single part, only one active mesh setup item of the type: " +
|
|||
|
|
"Shell gmsh, Tetrahedral gmsh, Transfinite mesh, Extrude mesh, Sweep mesh or Revolve mesh is possible.");
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
MeshSetupItem meshSetupItem = _gmshData.GmshSetupItems[0];
|
|||
|
|
|
|||
|
|
if (meshSetupItem is GmshSetupItem gsi)
|
|||
|
|
{
|
|||
|
|
Tuple<int, int>[] outDimTags;
|
|||
|
|
if (_isOCC)
|
|||
|
|
{
|
|||
|
|
// OpenCascade 几何
|
|||
|
|
Gmsh.Model.OCC.ImportShapes(_gmshData.GeometryFileName, out outDimTags, false, "");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// Discrete stl geometry
|
|||
|
|
double angleDeg = _gmshData.StlFeatureAngleDeg;
|
|||
|
|
if (angleDeg < 0)
|
|||
|
|
{
|
|||
|
|
angleDeg = 0;
|
|||
|
|
}
|
|||
|
|
double angleRad = angleDeg * Math.PI / 180;
|
|||
|
|
|
|||
|
|
Gmsh.Merge(_gmshData.GeometryFileName);
|
|||
|
|
|
|||
|
|
Gmsh.Model.Mesh.RemoveDuplicateNodes();
|
|||
|
|
Gmsh.Model.Mesh.ClassifySurfaces(angleRad, true, true, angleRad, true);
|
|||
|
|
Gmsh.Model.Mesh.CreateGeometry();
|
|||
|
|
|
|||
|
|
Gmsh.Model.GetEntities(out outDimTags, 2);
|
|||
|
|
|
|||
|
|
int[] surfaceIds = new int[outDimTags.Length];
|
|||
|
|
for (int i = 0; i < outDimTags.Length; i++)
|
|||
|
|
{
|
|||
|
|
surfaceIds[i] = outDimTags[i].Item2;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
int volumeId = Gmsh.Model.Geo.AddSurfaceLoop(surfaceIds);
|
|||
|
|
|
|||
|
|
Gmsh.Model.Geo.AddVolume(new int[] { volumeId });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// must be here
|
|||
|
|
Synchronize();
|
|||
|
|
|
|||
|
|
// Renumber Gmsh items
|
|||
|
|
RenumberGmshDataByCoor();
|
|||
|
|
|
|||
|
|
// PrepareData
|
|||
|
|
PrepareData(gsi);
|
|||
|
|
|
|||
|
|
// Mesh size
|
|||
|
|
SetMeshSizes(gsi.AlgorithmMesh2D);
|
|||
|
|
|
|||
|
|
// 2D meshing algorithm
|
|||
|
|
Gmsh.Option.SetNumber("Mesh.Algorithm", (int)gsi.AlgorithmMesh2D);
|
|||
|
|
|
|||
|
|
// 3D meshing algorithm
|
|||
|
|
Gmsh.Option.SetNumber("Mesh.Algorithm3D", (int)gsi.AlgorithmMesh3D);
|
|||
|
|
|
|||
|
|
// Recombine algorithm
|
|||
|
|
bool recombine = gsi.AlgorithmRecombine != GmshAlgorithmRecombineEnum.None;
|
|||
|
|
|
|||
|
|
if (recombine)
|
|||
|
|
{
|
|||
|
|
Gmsh.Option.SetNumber("Mesh.RecombinationAlgorithm", (int)gsi.AlgorithmRecombine);
|
|||
|
|
Gmsh.Option.SetNumber("Mesh.RecombineMinimumQuality", gsi.RecombineMinQuality);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Transfinite
|
|||
|
|
bool transfiniteVolume = gsi is TransfiniteMesh && gsi.TransfiniteThreeSided && gsi.TransfiniteFourSided;
|
|||
|
|
|
|||
|
|
bool sweepMesh = _gmshData.EdgeIdsBySweepLayer != null;
|
|||
|
|
if (_isOCC && (gsi.TransfiniteThreeSided || gsi.TransfiniteFourSided || sweepMesh))
|
|||
|
|
{
|
|||
|
|
//Gmsh.Mesh.SetTransfiniteAutomatic(gsi.TransfiniteAngleRad, recombine);
|
|||
|
|
SetTransfiniteSurfaces(gsi.TransfiniteThreeSided, gsi.TransfiniteFourSided, transfiniteVolume,
|
|||
|
|
recombine, gsi.AlgorithmRecombine);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Optimization On/Off
|
|||
|
|
int optimize = 1;
|
|||
|
|
if (gsi.OptimizeFirstOrderShell == GmshOptimizeFirstOrderShellEnum.None &&
|
|||
|
|
gsi.OptimizeFirstOrderSolid == GmshOptimizeFirstOrderSolidEnum.None &&
|
|||
|
|
gsi.OptimizeHighOrder == GmshOptimizeHighOrderEnum.None)
|
|||
|
|
{
|
|||
|
|
optimize = 0;
|
|||
|
|
}
|
|||
|
|
Gmsh.Option.SetNumber("Mesh.Optimize", optimize);
|
|||
|
|
|
|||
|
|
switch (gsi)
|
|||
|
|
{
|
|||
|
|
// 网格操作
|
|||
|
|
case ShellGmsh _:
|
|||
|
|
ShellGmsh(gsi, _gmshData.Preview);
|
|||
|
|
break;
|
|||
|
|
case TetrahedralGmsh _:
|
|||
|
|
TetrahedralGmsh(gsi, _gmshData.Preview);
|
|||
|
|
break;
|
|||
|
|
case TransfiniteMesh _:
|
|||
|
|
TransfiniteMesh(_gmshData.Preview);
|
|||
|
|
break;
|
|||
|
|
case ExtrudeMesh _:
|
|||
|
|
case RevolveMesh _:
|
|||
|
|
ExtrudeRevolveMesh(gsi, _gmshData.PartMeshingParameters, _gmshData.Preview);
|
|||
|
|
break;
|
|||
|
|
case SweepMesh sm:
|
|||
|
|
SweepMesh(sm, _gmshData.PartMeshingParameters, _gmshData.Preview);
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
throw new NotSupportedException("MeshSetupItemTypeException");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
throw new NotSupportedException("MeshSetupItemTypeException");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Element order
|
|||
|
|
if (!_gmshData.Preview && _gmshData.PartMeshingParameters.SecondOrder)
|
|||
|
|
{
|
|||
|
|
if (!_gmshData.PartMeshingParameters.MidsideNodesOnGeometry)
|
|||
|
|
{
|
|||
|
|
Gmsh.Option.SetNumber("Mesh.SecondOrderLinear", 1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Create incomplete second order elements: 8-node quads, 20-node hexas, etc.
|
|||
|
|
Gmsh.Option.SetNumber("Mesh.SecondOrderIncomplete", 1); // second
|
|||
|
|
|
|||
|
|
Gmsh.Model.Mesh.SetOrder(2); // third
|
|||
|
|
|
|||
|
|
// Optimize high order
|
|||
|
|
if (gsi.OptimizeHighOrder != GmshOptimizeHighOrderEnum.None)
|
|||
|
|
{
|
|||
|
|
Tuple<int, int>[] dimTags = new Tuple<int, int>[0];
|
|||
|
|
Gmsh.Model.Mesh.Optimize(gsi.OptimizeHighOrder.ToString(), false, 1, dimTags);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Output
|
|||
|
|
Gmsh.Write(_gmshData.InpFileName);
|
|||
|
|
//Gmsh.Write(@"c:\Temp\mesh.mesh");
|
|||
|
|
|
|||
|
|
// 输出到控制台
|
|||
|
|
_writeOutput?.Invoke("Meshing done.");
|
|||
|
|
_writeOutput?.Invoke("");
|
|||
|
|
double elapsedTime = Gmsh.Option.GetNumber("Mesh.CpuTime");
|
|||
|
|
_writeOutput?.Invoke("Elapsed time [s]: " + Math.Round(elapsedTime, 5));
|
|||
|
|
_writeOutput?.Invoke("");
|
|||
|
|
|
|||
|
|
// 错误代码
|
|||
|
|
_error = null;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_error = ex.Message;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void SetMeshSizes(GmshAlgorithmMesh2DEnum algorithm2D)
|
|||
|
|
{
|
|||
|
|
// Mesh size
|
|||
|
|
//Tuple<int, int>[] surfaceDimTags;
|
|||
|
|
//Gmsh.GetEntities(out surfaceDimTags, 2);
|
|||
|
|
//foreach (var surfaceDimTag in surfaceDimTags) Gmsh.Mesh.SetSizeFromBoundary(2, surfaceDimTag.Item2, 0);
|
|||
|
|
//
|
|||
|
|
double sf = 1;
|
|||
|
|
if (algorithm2D == GmshAlgorithmMesh2DEnum.QuasiStructuredQuad) sf = 2;
|
|||
|
|
//
|
|||
|
|
Gmsh.Option.SetNumber("Mesh.MeshSizeMin", _gmshData.PartMeshingParameters.MinH * sf);
|
|||
|
|
Gmsh.Option.SetNumber("Mesh.MeshSizeMax", _gmshData.PartMeshingParameters.MaxH * sf);
|
|||
|
|
double cs = 2 * Math.PI * _gmshData.PartMeshingParameters.ElementsPerCurve / sf;
|
|||
|
|
Gmsh.Option.SetNumber("Mesh.MeshSizeFromCurvature", cs);
|
|||
|
|
// Local vertex mesh size
|
|||
|
|
Tuple<int, int>[] dimTags = new Tuple<int, int>[1];
|
|||
|
|
foreach (var entry in _gmshData.VertexNodeIdMeshSize)
|
|||
|
|
{
|
|||
|
|
dimTags[0] = new Tuple<int, int>(0, entry.Key);
|
|||
|
|
Gmsh.Model.OCC.SetSize(dimTags, entry.Value);
|
|||
|
|
}
|
|||
|
|
// Local edge mesh size
|
|||
|
|
int edgeId;
|
|||
|
|
int numOfElements;
|
|||
|
|
// Get all edges
|
|||
|
|
Gmsh.Model.GetEntities(out dimTags, 1);
|
|||
|
|
// Check all edges
|
|||
|
|
Dictionary<int, int> edgeIdNumberOfElements = new Dictionary<int, int>();
|
|||
|
|
foreach (var entry in dimTags)
|
|||
|
|
{
|
|||
|
|
edgeId = entry.Item2;
|
|||
|
|
//
|
|||
|
|
if (_gmshData.EdgeIdNumElements.TryGetValue(edgeId, out numOfElements))
|
|||
|
|
{
|
|||
|
|
numOfElements = (int)(numOfElements / sf);
|
|||
|
|
edgeIdNumberOfElements[edgeId] = numOfElements;
|
|||
|
|
// Sweep edge sizes
|
|||
|
|
if (_gmshData.EdgeIdsBySweepLayer != null)
|
|||
|
|
{
|
|||
|
|
foreach (var edgeIds in _gmshData.EdgeIdsBySweepLayer)
|
|||
|
|
{
|
|||
|
|
if (edgeIds.Contains(edgeId))
|
|||
|
|
{
|
|||
|
|
foreach (var sweepEdgeId in edgeIds)
|
|||
|
|
{
|
|||
|
|
edgeIdNumberOfElements[sweepEdgeId] = numOfElements;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Overwrite all edge sizes
|
|||
|
|
int numOfNodes;
|
|||
|
|
foreach (var entry in edgeIdNumberOfElements)
|
|||
|
|
{
|
|||
|
|
edgeId = entry.Key;
|
|||
|
|
numOfElements = entry.Value;
|
|||
|
|
//
|
|||
|
|
_gmshData.EdgeIdNumElements[edgeId] = numOfElements;
|
|||
|
|
//
|
|||
|
|
numOfNodes = numOfElements + 1;
|
|||
|
|
Gmsh.Model.Mesh.SetTransfiniteCurve(edgeId, numOfNodes);
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
Synchronize(); // must be here for mesh refinement
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成壳单元
|
|||
|
|
private void ShellGmsh(GmshSetupItem gmshSetupItem, bool preview)
|
|||
|
|
{
|
|||
|
|
bool recombine = gmshSetupItem.AlgorithmRecombine != GmshAlgorithmRecombineEnum.None;
|
|||
|
|
// Recombine all
|
|||
|
|
if (recombine) Gmsh.Option.SetNumber("Mesh.RecombineAll", 1);
|
|||
|
|
|
|||
|
|
// 生成网格
|
|||
|
|
if (preview)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.Mesh.Generate(2);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.Mesh.Generate(2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Optimize first order
|
|||
|
|
if (gmshSetupItem.OptimizeFirstOrderShell != GmshOptimizeFirstOrderShellEnum.None)
|
|||
|
|
{
|
|||
|
|
Tuple<int, int>[] dimTags = new Tuple<int, int>[0];
|
|||
|
|
Gmsh.Model.Mesh.Optimize(gmshSetupItem.OptimizeFirstOrderShell.ToString(), false, 10, dimTags);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成四面体单元
|
|||
|
|
private void TetrahedralGmsh(GmshSetupItem gmshSetupItem, bool preview)
|
|||
|
|
{
|
|||
|
|
bool recombine = gmshSetupItem.AlgorithmRecombine != GmshAlgorithmRecombineEnum.None;
|
|||
|
|
|
|||
|
|
// Recombine all
|
|||
|
|
if (recombine)
|
|||
|
|
{
|
|||
|
|
Gmsh.Option.SetNumber("Mesh.RecombineAll", 1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 生成网格
|
|||
|
|
if (preview)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.Mesh.Generate(1);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.Mesh.Generate(3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Optimize first order
|
|||
|
|
if (gmshSetupItem.OptimizeFirstOrderSolid != GmshOptimizeFirstOrderSolidEnum.None)
|
|||
|
|
{
|
|||
|
|
Tuple<int, int>[] dimTags = new Tuple<int, int>[0];
|
|||
|
|
Gmsh.Model.Mesh.Optimize(gmshSetupItem.OptimizeFirstOrderSolid.ToString(), false, 10, dimTags);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void TransfiniteMesh(bool preview)
|
|||
|
|
{
|
|||
|
|
if (preview)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.Mesh.Generate(1);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.Mesh.Generate(3);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ExtrudeRevolveMesh(GmshSetupItem gmshSetupItem, MeshingParameters meshingParameters, bool preview)
|
|||
|
|
{
|
|||
|
|
ExtrudeMesh extrudeMesh = null;
|
|||
|
|
RevolveMesh revolveMesh = null;
|
|||
|
|
//
|
|||
|
|
if (gmshSetupItem is ExtrudeMesh em) extrudeMesh = em;
|
|||
|
|
else if (gmshSetupItem is RevolveMesh rm) revolveMesh = rm;
|
|||
|
|
else throw new NotSupportedException();
|
|||
|
|
//
|
|||
|
|
if (extrudeMesh != null)
|
|||
|
|
{
|
|||
|
|
if (extrudeMesh.ExtrudeCenter == null || extrudeMesh.Direction == null)
|
|||
|
|
throw new CaeException("The extrude direction could not be determined.");
|
|||
|
|
}
|
|||
|
|
else if (revolveMesh != null)
|
|||
|
|
{
|
|||
|
|
if (revolveMesh.AxisCenter == null || revolveMesh.AxisDirection == null)
|
|||
|
|
throw new CaeException("The revolve direction could not be determined.");
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
int surfaceId;
|
|||
|
|
HashSet<int> allExtrudeSurfaceIds = new HashSet<int>();
|
|||
|
|
bool recombine = gmshSetupItem.AlgorithmRecombine != GmshAlgorithmRecombineEnum.None;
|
|||
|
|
Tuple<int, int>[] extrudeDimTags = new Tuple<int, int>[gmshSetupItem.CreationIds.Length];
|
|||
|
|
// Collect surfaces
|
|||
|
|
for (int i = 0; i < gmshSetupItem.CreationIds.Length; i++)
|
|||
|
|
{
|
|||
|
|
surfaceId = FeMesh.GetItemIdFromGeometryId(gmshSetupItem.CreationIds[i]) + 1;
|
|||
|
|
extrudeDimTags[i] = new Tuple<int, int>(2, surfaceId);
|
|||
|
|
allExtrudeSurfaceIds.Add(surfaceId);
|
|||
|
|
// Recombine surfaces
|
|||
|
|
if (recombine) Gmsh.Model.Mesh.SetRecombine(2, surfaceId);
|
|||
|
|
}
|
|||
|
|
// Layers - size
|
|||
|
|
int numEl;
|
|||
|
|
int[] numElements;
|
|||
|
|
double[] height;
|
|||
|
|
if (gmshSetupItem.ElementSizeType == ElementSizeTypeEnum.ScaleFactor)
|
|||
|
|
{
|
|||
|
|
double edgeLength;
|
|||
|
|
if (extrudeMesh != null)
|
|||
|
|
{
|
|||
|
|
edgeLength = Math.Sqrt(Math.Pow(extrudeMesh.Direction[0], 2) +
|
|||
|
|
Math.Pow(extrudeMesh.Direction[1], 2) +
|
|||
|
|
Math.Pow(extrudeMesh.Direction[2], 2));
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
else if (revolveMesh != null)
|
|||
|
|
{
|
|||
|
|
edgeLength = revolveMesh.MiddleR * revolveMesh.AngleDeg * Math.PI / 180;
|
|||
|
|
}
|
|||
|
|
else throw new NotSupportedException();
|
|||
|
|
//
|
|||
|
|
numEl = (int)Math.Round(edgeLength / meshingParameters.MaxH / gmshSetupItem.ElementScaleFactor, 0);
|
|||
|
|
if (numEl < 1) numEl = 1;
|
|||
|
|
numElements = new int[] { numEl };
|
|||
|
|
height = new double[] { 1 };
|
|||
|
|
}
|
|||
|
|
else if (gmshSetupItem.ElementSizeType == ElementSizeTypeEnum.NumberOfElements)
|
|||
|
|
{
|
|||
|
|
numEl = gmshSetupItem.NumberOfElements;
|
|||
|
|
if (numEl < 1) numEl = 1;
|
|||
|
|
numElements = new int[] { numEl };
|
|||
|
|
height = new double[] { 1 };
|
|||
|
|
}
|
|||
|
|
else if (gmshSetupItem.ElementSizeType == ElementSizeTypeEnum.MultiLayerd)
|
|||
|
|
{
|
|||
|
|
numElements = gmshSetupItem.NumOfElementsPerLayer;
|
|||
|
|
//
|
|||
|
|
double sumSizes = 0;
|
|||
|
|
for (int i = 0; i < gmshSetupItem.LayerSizes.Length; i++) sumSizes += gmshSetupItem.LayerSizes[i];
|
|||
|
|
//
|
|||
|
|
double sumHeight = 0;
|
|||
|
|
height = new double[gmshSetupItem.LayerSizes.Length];
|
|||
|
|
for (int i = 0; i < height.Length; i++)
|
|||
|
|
{
|
|||
|
|
sumHeight += gmshSetupItem.LayerSizes[i] / sumSizes;
|
|||
|
|
height[i] = sumHeight;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else throw new NotSupportedException("ExtrudedElementSizeTypeEnumException");
|
|||
|
|
//
|
|||
|
|
if (preview)
|
|||
|
|
{
|
|||
|
|
Tuple<int, int>[] allDimTags = Gmsh.Model.OCC.GetEntities(2);
|
|||
|
|
List<Tuple<int, int>> toRemoveDimTags = new List<Tuple<int, int>>() { new Tuple<int, int>(3, 1) }; // add solid
|
|||
|
|
for (int i = 0; i < allDimTags.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (!allExtrudeSurfaceIds.Contains(allDimTags[i].Item2)) toRemoveDimTags.Add(allDimTags[i]);
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.OCC.Remove(toRemoveDimTags.ToArray(), false);
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.OCC.Synchronize(); // must be here
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.Mesh.Generate(2);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Tuple<int, int>[] outDimTags;
|
|||
|
|
// Extrude
|
|||
|
|
if (extrudeMesh != null)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.OCC.Extrude(extrudeDimTags,
|
|||
|
|
extrudeMesh.Direction[0],
|
|||
|
|
extrudeMesh.Direction[1],
|
|||
|
|
extrudeMesh.Direction[2],
|
|||
|
|
out outDimTags, numElements, height, true);
|
|||
|
|
}
|
|||
|
|
// Revolve
|
|||
|
|
else if (revolveMesh != null)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.OCC.Revolve(extrudeDimTags,
|
|||
|
|
revolveMesh.AxisCenter[0],
|
|||
|
|
revolveMesh.AxisCenter[1],
|
|||
|
|
revolveMesh.AxisCenter[2],
|
|||
|
|
revolveMesh.AxisDirection[0],
|
|||
|
|
revolveMesh.AxisDirection[1],
|
|||
|
|
revolveMesh.AxisDirection[2],
|
|||
|
|
revolveMesh.AngleDeg * Math.PI / 180,
|
|||
|
|
out outDimTags, numElements, height, true);
|
|||
|
|
}
|
|||
|
|
else throw new NotSupportedException();
|
|||
|
|
// Volume check
|
|||
|
|
if (CheckMeshVolume(outDimTags))
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.OCC.Remove(new Tuple<int, int>[] { new Tuple<int, int>(3, 1) }, true);
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.OCC.Synchronize(); // must be here
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.Mesh.Generate(3);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
throw new CaeException("The volume of the extruded mesh is not equal to the volume " +
|
|||
|
|
"of the geometry it represents. Try selecting other surfaces for the extrusion.");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
private void SweepMesh(SweepMesh sweepMesh, MeshingParameters meshingParameters, bool preview)
|
|||
|
|
{
|
|||
|
|
if (sweepMesh == null) throw new NotSupportedException();
|
|||
|
|
//
|
|||
|
|
int surfaceId;
|
|||
|
|
HashSet<int> sourceSurfaceIds = new HashSet<int>();
|
|||
|
|
bool recombine = sweepMesh.AlgorithmRecombine != GmshAlgorithmRecombineEnum.None;
|
|||
|
|
// Get source surfaces
|
|||
|
|
for (int i = 0; i < sweepMesh.CreationIds.Length; i++)
|
|||
|
|
{
|
|||
|
|
surfaceId = FeMesh.GetItemIdFromGeometryId(sweepMesh.CreationIds[i]) + 1;
|
|||
|
|
sourceSurfaceIds.Add(surfaceId);
|
|||
|
|
//Gmsh.Model.Mesh.SetSmoothing(2, surfaceId, 100); // smoothing
|
|||
|
|
}
|
|||
|
|
// Get surface item ids
|
|||
|
|
Dictionary<int, int[]> surfaceIdEdgeIds;
|
|||
|
|
Dictionary<int, int[]> surfaceIdVertexIds;
|
|||
|
|
GetSurfaceItems(out surfaceIdEdgeIds, out surfaceIdVertexIds);
|
|||
|
|
// Get side surfaces
|
|||
|
|
HashSet<int> sideSurfaceIds = new HashSet<int>(sweepMesh.SideSurfaceIds);
|
|||
|
|
HashSet<int> targetSurfaceIds = new HashSet<int>();
|
|||
|
|
// Get target surfaces
|
|||
|
|
targetSurfaceIds.UnionWith(surfaceIdVertexIds.Keys);
|
|||
|
|
targetSurfaceIds.ExceptWith(sourceSurfaceIds);
|
|||
|
|
targetSurfaceIds.ExceptWith(sideSurfaceIds);
|
|||
|
|
if (targetSurfaceIds.Count != 1) throw new NotSupportedException();
|
|||
|
|
// Set source surfaces
|
|||
|
|
if (recombine)
|
|||
|
|
{
|
|||
|
|
foreach (var sourceSurfaceId in sourceSurfaceIds) Gmsh.Model.Mesh.SetRecombine(2, sourceSurfaceId);
|
|||
|
|
}
|
|||
|
|
// Set side surfaces
|
|||
|
|
foreach (var sideSurfaceId in sideSurfaceIds)
|
|||
|
|
{
|
|||
|
|
//Gmsh.Model.Mesh.SetAlgorithm(2, sideSurfaceId, (int)GmshAlgorithmMesh2DEnum.FrontalDelaunay);
|
|||
|
|
Gmsh.Model.Mesh.SetRecombine(2, sideSurfaceId);
|
|||
|
|
Gmsh.Model.Mesh.SetTransfiniteSurface(sideSurfaceId, "AlternateLeft");
|
|||
|
|
//Gmsh.Model.Mesh.SetSmoothing(2, sideSurfaceId, 100); // smoothing
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.Mesh.Generate(2);
|
|||
|
|
// Remove the volume and the target surface mesh
|
|||
|
|
List<Tuple<int, int>> toRemoveDimTags = new List<Tuple<int, int>>() { new Tuple<int, int>(3, 1) }; // volume
|
|||
|
|
foreach (var targetSurfaceId in targetSurfaceIds) toRemoveDimTags.Add(new Tuple<int, int>(2, targetSurfaceId));
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.Mesh.Clear(toRemoveDimTags.ToArray());
|
|||
|
|
// Optimize first order
|
|||
|
|
if (sweepMesh.OptimizeFirstOrderShell != GmshOptimizeFirstOrderShellEnum.None)
|
|||
|
|
{
|
|||
|
|
Tuple<int, int>[] dimTags = new Tuple<int, int>[0];
|
|||
|
|
Gmsh.Model.Mesh.Optimize(sweepMesh.OptimizeFirstOrderShell.ToString(), false, 10, dimTags);
|
|||
|
|
}
|
|||
|
|
// Debug
|
|||
|
|
if (System.Diagnostics.Debugger.IsAttached) Gmsh.Write(_gmshData.InpFileName);
|
|||
|
|
//
|
|||
|
|
if (preview)
|
|||
|
|
{
|
|||
|
|
// Remove the side surface mesh
|
|||
|
|
toRemoveDimTags = new List<Tuple<int, int>>();
|
|||
|
|
foreach (var sideSurfaceId in sideSurfaceIds) toRemoveDimTags.Add(new Tuple<int, int>(2, sideSurfaceId));
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.Mesh.Clear(toRemoveDimTags.ToArray());
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
SweepMethods.CreateSweepMesh(sourceSurfaceIds, sideSurfaceIds, targetSurfaceIds,
|
|||
|
|
sweepMesh.NumberOfLayerSmoothSteps, sweepMesh.NumberOfGlobalSmoothSteps,
|
|||
|
|
surfaceIdEdgeIds, surfaceIdVertexIds);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//
|
|||
|
|
public bool CheckMeshVolume(Tuple<int, int>[] outDimTags)
|
|||
|
|
{
|
|||
|
|
double volumeOut;
|
|||
|
|
double initialVolume;
|
|||
|
|
double extrudedVolume = 0;
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.OCC.GetMass(3, 1, out initialVolume);
|
|||
|
|
//
|
|||
|
|
foreach (var outDimTag in outDimTags)
|
|||
|
|
{
|
|||
|
|
if (outDimTag.Item1 == 3)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.OCC.GetMass(3, outDimTag.Item2, out volumeOut);
|
|||
|
|
extrudedVolume += volumeOut;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
double maxVolume = Math.Max(initialVolume, extrudedVolume);
|
|||
|
|
if (Math.Abs(initialVolume - extrudedVolume) > 1E-2 * maxVolume) return false;
|
|||
|
|
//
|
|||
|
|
return true;
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
public bool CheckMeshVolume(GeometryPart part, ExtrudeMesh extrudeMesh,
|
|||
|
|
Func<GeometryPart, string> ExportCADPartGeometryToDefaultFile)
|
|||
|
|
{
|
|||
|
|
int surfaceId;
|
|||
|
|
string brepFileName = ExportCADPartGeometryToDefaultFile(part);
|
|||
|
|
// Initialize
|
|||
|
|
Gmsh.Initialize();
|
|||
|
|
Gmsh.Model.Add("Brep_model");
|
|||
|
|
// Import
|
|||
|
|
Tuple<int, int>[] outDimTags;
|
|||
|
|
Gmsh.Model.OCC.ImportShapes(brepFileName, out outDimTags, false, "");
|
|||
|
|
Gmsh.Model.OCC.Synchronize(); // must be here
|
|||
|
|
// Get surfaces to extrude
|
|||
|
|
Tuple<int, int>[] dimTags = new Tuple<int, int>[extrudeMesh.CreationIds.Length];
|
|||
|
|
for (int i = 0; i < extrudeMesh.CreationIds.Length; i++)
|
|||
|
|
{
|
|||
|
|
surfaceId = FeMesh.GetItemIdFromGeometryId(extrudeMesh.CreationIds[i]) + 1;
|
|||
|
|
dimTags[i] = new Tuple<int, int>(2, surfaceId);
|
|||
|
|
}
|
|||
|
|
// Setup layers
|
|||
|
|
int[] numElements = new int[] { 1 };
|
|||
|
|
double[] height = new double[] { 1 };
|
|||
|
|
// Extrude
|
|||
|
|
Gmsh.Model.OCC.Extrude(dimTags, 0, 0, -50, out outDimTags, numElements, height, false);
|
|||
|
|
//
|
|||
|
|
double massOut;
|
|||
|
|
double mass1 = 0;
|
|||
|
|
double massExtruded = 0;
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.OCC.GetMass(3, 1, out massOut);
|
|||
|
|
mass1 += massOut;
|
|||
|
|
//
|
|||
|
|
foreach (var outDimTag in outDimTags)
|
|||
|
|
{
|
|||
|
|
if (outDimTag.Item1 == 3)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.OCC.GetMass(3, outDimTag.Item2, out massOut);
|
|||
|
|
massExtruded += massOut;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static void GetSurfaceItems(out Dictionary<int, int[]> surfaceIdEdgeIds,
|
|||
|
|
out Dictionary<int, int[]> surfaceIdVertexIds)
|
|||
|
|
{
|
|||
|
|
Tuple<int, int>[] surfaceDimTags = Gmsh.Model.OCC.GetEntities(2);
|
|||
|
|
//
|
|||
|
|
int[] edgeIds;
|
|||
|
|
int[] vertexIds;
|
|||
|
|
HashSet<int> allVertexIds;
|
|||
|
|
surfaceIdEdgeIds = new Dictionary<int, int[]>();
|
|||
|
|
surfaceIdVertexIds = new Dictionary<int, int[]>();
|
|||
|
|
//
|
|||
|
|
foreach (var surfaceDimTag in surfaceDimTags)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.GetAdjacencies(2, surfaceDimTag.Item2, out _, out edgeIds);
|
|||
|
|
surfaceIdEdgeIds.Add(surfaceDimTag.Item2, edgeIds);
|
|||
|
|
//
|
|||
|
|
allVertexIds = new HashSet<int>();
|
|||
|
|
foreach (var edgeId in edgeIds)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.GetAdjacencies(1, edgeId, out _, out vertexIds);
|
|||
|
|
allVertexIds.UnionWith(vertexIds);
|
|||
|
|
}
|
|||
|
|
surfaceIdVertexIds.Add(surfaceDimTag.Item2, allVertexIds.ToArray());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void SetTransfiniteSurfaces(bool transfiniteThreeSided, bool transfiniteFourSided, bool transfiniteVolume,
|
|||
|
|
bool recombine, GmshAlgorithmRecombineEnum recombineAlgorithm)
|
|||
|
|
{
|
|||
|
|
int[] volumeIds;
|
|||
|
|
int[] edgeIds;
|
|||
|
|
int[][] edgeVertexIds;
|
|||
|
|
int[] vertexIds;
|
|||
|
|
HashSet<int> surfaceVertexIds = new HashSet<int>();
|
|||
|
|
//bool invertEdge;
|
|||
|
|
int edgeDim = 1;
|
|||
|
|
int edgeId;
|
|||
|
|
int surfaceDim = 2;
|
|||
|
|
int surfaceId;
|
|||
|
|
int volumeId;
|
|||
|
|
double edgeLength;
|
|||
|
|
double[] edgeLengths;
|
|||
|
|
//
|
|||
|
|
GmshEdge edge;
|
|||
|
|
GmshEdge existingEdge;
|
|||
|
|
Dictionary<int, GmshEdge> edgeIdEdge = new Dictionary<int, GmshEdge>();
|
|||
|
|
GmshSurface surface;
|
|||
|
|
Dictionary<int, GmshSurface> surfaceIdSurface = new Dictionary<int, GmshSurface>();
|
|||
|
|
GmshVolume volume;
|
|||
|
|
GmshVolume existingVolume;
|
|||
|
|
Dictionary<int, GmshVolume> volumeIdVolume = new Dictionary<int, GmshVolume>();
|
|||
|
|
//
|
|||
|
|
Tuple<int, int>[] vertexDimTags;
|
|||
|
|
Tuple<int, int>[] edgeDimTags;
|
|||
|
|
Tuple<int, int>[] surfaceDimTags;
|
|||
|
|
Tuple<int, int>[] volumeDimTags;
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.GetEntities(out vertexDimTags, 0);
|
|||
|
|
Gmsh.Model.GetEntities(out edgeDimTags, 1);
|
|||
|
|
Gmsh.Model.GetEntities(out surfaceDimTags, 2);
|
|||
|
|
Gmsh.Model.GetEntities(out volumeDimTags, 3);
|
|||
|
|
// Collect volume faces
|
|||
|
|
foreach (var surfaceDimTag in surfaceDimTags)
|
|||
|
|
{
|
|||
|
|
surfaceId = surfaceDimTag.Item2;
|
|||
|
|
if (surfaceIdSurface.ContainsKey(surfaceId)) continue;
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.GetAdjacencies(surfaceDim, surfaceId, out volumeIds, out edgeIds);
|
|||
|
|
// Get edge orientations
|
|||
|
|
//Gmsh.GetBoundary(new Tuple<int, int>[] { new Tuple<int, int>(surfaceDim, surfaceId) },
|
|||
|
|
// out edgeDimTags, false, true, false);
|
|||
|
|
// Surface
|
|||
|
|
surfaceVertexIds.Clear();
|
|||
|
|
edgeVertexIds = new int[edgeIds.Length][];
|
|||
|
|
edgeLengths = new double[edgeIds.Length];
|
|||
|
|
for (int j = 0; j < edgeIds.Length; j++)
|
|||
|
|
{
|
|||
|
|
edgeId = edgeIds[j];
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.GetAdjacencies(edgeDim, edgeId, out _, out vertexIds);
|
|||
|
|
//
|
|||
|
|
Array.Sort(vertexIds);
|
|||
|
|
//
|
|||
|
|
if (_isOCC) Gmsh.Model.OCC.GetMass(edgeDim, edgeId, out edgeLength);
|
|||
|
|
else edgeLength = 1;
|
|||
|
|
edge = new GmshEdge(edgeId, vertexIds, surfaceId, edgeLength);
|
|||
|
|
edgeLengths[j] = edgeLength;
|
|||
|
|
//
|
|||
|
|
if (edgeIdEdge.TryGetValue(edgeId, out existingEdge))
|
|||
|
|
existingEdge.SurfaceIds.UnionWith(edge.SurfaceIds);
|
|||
|
|
else edgeIdEdge.Add(edge.Id, edge);
|
|||
|
|
//
|
|||
|
|
edgeVertexIds[j] = edge.VertexIds;
|
|||
|
|
surfaceVertexIds.UnionWith(vertexIds);
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
for (int i = 0; i < volumeIds.Length; i++)
|
|||
|
|
{
|
|||
|
|
volumeId = volumeIds[i];
|
|||
|
|
volume = new GmshVolume(volumeId, surfaceId);
|
|||
|
|
//
|
|||
|
|
if (volumeIdVolume.TryGetValue(volumeId, out existingVolume))
|
|||
|
|
existingVolume.SurfaceIds.UnionWith(volume.SurfaceIds);
|
|||
|
|
else volumeIdVolume.Add(volume.Id, volume);
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
surface = new GmshSurface(surfaceId, surfaceVertexIds.ToArray(), edgeIds, edgeLengths, edgeVertexIds);
|
|||
|
|
surfaceIdSurface.Add(surface.Id, surface);
|
|||
|
|
}
|
|||
|
|
// Check if a volume is a transfinite volume
|
|||
|
|
foreach (var entry in volumeIdVolume)
|
|||
|
|
{
|
|||
|
|
volumeId = entry.Key;
|
|||
|
|
volume = entry.Value;
|
|||
|
|
//
|
|||
|
|
if (!transfiniteVolume) volume.Transfinite = false;
|
|||
|
|
else if (volumeId > 0) volume.Transfinite = true;
|
|||
|
|
else volume.Transfinite = false;
|
|||
|
|
//
|
|||
|
|
if (volume.SurfaceIds.Count == 5 || volume.SurfaceIds.Count == 6)
|
|||
|
|
{
|
|||
|
|
foreach (var volSurfaceId in volume.SurfaceIds)
|
|||
|
|
{
|
|||
|
|
surface = surfaceIdSurface[volSurfaceId];
|
|||
|
|
if (surface.Transfinite)
|
|||
|
|
{
|
|||
|
|
if (surface.ThreeSided && transfiniteThreeSided) volume.TriSurfIds.Add(volSurfaceId);
|
|||
|
|
else if (surface.FourSided && transfiniteFourSided) volume.QuadSurfIds.Add(volSurfaceId);
|
|||
|
|
}
|
|||
|
|
else { volume.Transfinite = false; break; }
|
|||
|
|
}
|
|||
|
|
if (volume.Transfinite)
|
|||
|
|
{
|
|||
|
|
if (!(volume.NumQuadSurfaces == 6 || (volume.NumTriSurfaces == 2 && volume.NumQuadSurfaces == 3)))
|
|||
|
|
volume.Transfinite = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else volume.Transfinite = false;
|
|||
|
|
//
|
|||
|
|
if (volume.Transfinite)
|
|||
|
|
{
|
|||
|
|
foreach (var volumeSurfaceId in volume.SurfaceIds) surfaceIdSurface[volumeSurfaceId].Recombine = true;
|
|||
|
|
}
|
|||
|
|
// Fix 5 sided volumes
|
|||
|
|
if (volume.Transfinite && volume.SurfaceIds.Count == 5)
|
|||
|
|
{
|
|||
|
|
HashSet<int> connectingEdgeIds = new HashSet<int>();
|
|||
|
|
foreach (var quadSurfaceId in volume.QuadSurfIds)
|
|||
|
|
connectingEdgeIds.UnionWith(surfaceIdSurface[quadSurfaceId].EdgeIds);
|
|||
|
|
foreach (var triSurfaceId in volume.TriSurfIds)
|
|||
|
|
connectingEdgeIds.ExceptWith(surfaceIdSurface[triSurfaceId].EdgeIds);
|
|||
|
|
//
|
|||
|
|
surface = surfaceIdSurface[volume.TriSurfIds[0]];
|
|||
|
|
int firstSurfaceVertexId = surface.VertexIds[0];
|
|||
|
|
int secondSurfaceVertexId = -1;
|
|||
|
|
//
|
|||
|
|
foreach (var connectingEdgeId in connectingEdgeIds)
|
|||
|
|
{
|
|||
|
|
edge = edgeIdEdge[connectingEdgeId];
|
|||
|
|
if (edge.VertexIds[0] == firstSurfaceVertexId) { secondSurfaceVertexId = edge.VertexIds[1]; break; }
|
|||
|
|
else if (edge.VertexIds[1] == firstSurfaceVertexId) { secondSurfaceVertexId = edge.VertexIds[0]; break; }
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
if (secondSurfaceVertexId != -1)
|
|||
|
|
{
|
|||
|
|
surface = surfaceIdSurface[volume.TriSurfIds[1]];
|
|||
|
|
surface.SetFirstVertexId(secondSurfaceVertexId);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Edge graph
|
|||
|
|
Node<GmshEdge> edgeNode;
|
|||
|
|
Graph<GmshEdge> edgeGraph = new Graph<GmshEdge>();
|
|||
|
|
Dictionary<int, Node<GmshEdge>> edgeIdNodeEdge = new Dictionary<int, Node<GmshEdge>>();
|
|||
|
|
// Add opposite edges as connections to graph
|
|||
|
|
foreach (var entry in surfaceIdSurface)
|
|||
|
|
{
|
|||
|
|
surface = entry.Value;
|
|||
|
|
if (surface.Transfinite)
|
|||
|
|
{
|
|||
|
|
// Add nodes to graph
|
|||
|
|
for (int i = 0; i < surface.EdgeIds.Length; i++)
|
|||
|
|
{
|
|||
|
|
edgeId = surface.EdgeIds[i];
|
|||
|
|
if (!edgeIdNodeEdge.ContainsKey(edgeId))
|
|||
|
|
{
|
|||
|
|
edgeNode = new Node<GmshEdge>(edgeIdEdge[edgeId]);
|
|||
|
|
edgeIdNodeEdge.Add(edgeId, edgeNode);
|
|||
|
|
edgeGraph.AddNode(edgeNode);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Add connections to graph
|
|||
|
|
edgeGraph.AddUndirectedEdge(edgeIdNodeEdge[surface.OppositeEdgesA[0]],
|
|||
|
|
edgeIdNodeEdge[surface.OppositeEdgesA[1]]);
|
|||
|
|
if (surface.OppositeEdgesB != null)
|
|||
|
|
{
|
|||
|
|
edgeGraph.AddUndirectedEdge(edgeIdNodeEdge[surface.OppositeEdgesB[0]],
|
|||
|
|
edgeIdNodeEdge[surface.OppositeEdgesB[1]]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
// Get groups of connected edges
|
|||
|
|
List<Graph<GmshEdge>> edgeGroups = edgeGraph.GetConnectedSubgraphs();
|
|||
|
|
//
|
|||
|
|
int min;
|
|||
|
|
int max;
|
|||
|
|
int sum;
|
|||
|
|
int maxByRefinement;
|
|||
|
|
int numOfElements;
|
|||
|
|
int numOfNodes;
|
|||
|
|
int avgNumOfNodes;
|
|||
|
|
HashSet<int> groupEdgeIds;
|
|||
|
|
IntPtr[] nodeTagsIntPtr;
|
|||
|
|
double[] coor;
|
|||
|
|
// Create edge mesh to get the number of nodes for each edge
|
|||
|
|
Gmsh.Model.Mesh.Generate(1);
|
|||
|
|
//
|
|||
|
|
Dictionary<int, int> edgeIdNumOfNodes = new Dictionary<int, int>();
|
|||
|
|
foreach (var edgeGroup in edgeGroups)
|
|||
|
|
{
|
|||
|
|
if (edgeGroup.Nodes.Count <= 1) continue;
|
|||
|
|
//
|
|||
|
|
min = int.MaxValue;
|
|||
|
|
max = -int.MaxValue;
|
|||
|
|
maxByRefinement = -int.MaxValue;
|
|||
|
|
sum = 0;
|
|||
|
|
groupEdgeIds = new HashSet<int>();
|
|||
|
|
//
|
|||
|
|
foreach (var edgeNodeFromGroup in edgeGroup.Nodes)
|
|||
|
|
{
|
|||
|
|
// Mesh refinement
|
|||
|
|
if (_gmshData.EdgeIdNumElements.TryGetValue(edgeNodeFromGroup.Value.Id, out numOfElements)) // must be here
|
|||
|
|
{
|
|||
|
|
numOfNodes = numOfElements + 1;
|
|||
|
|
if (numOfNodes > maxByRefinement) maxByRefinement = numOfNodes;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// Only consider if there is no refinement found jet
|
|||
|
|
if (maxByRefinement < 0)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.Mesh.GetNodes(out nodeTagsIntPtr, out coor, 1, edgeNodeFromGroup.Value.Id, true, false);
|
|||
|
|
//
|
|||
|
|
numOfNodes = nodeTagsIntPtr.Length; // for 2 nodes there is only 1 element
|
|||
|
|
if (numOfNodes < min) min = numOfNodes;
|
|||
|
|
if (numOfNodes > max) max = numOfNodes;
|
|||
|
|
sum += numOfNodes;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
if (maxByRefinement > 0) avgNumOfNodes = maxByRefinement;
|
|||
|
|
else avgNumOfNodes = (int)Math.Round((double)sum / edgeGroup.Nodes.Count, 0, MidpointRounding.AwayFromZero);
|
|||
|
|
//avgNumOfNodes = (int)Math.Round((min + max) / 2.0, 0, MidpointRounding.AwayFromZero);
|
|||
|
|
//
|
|||
|
|
foreach (var edgeNodeFromGroup in edgeGroup.Nodes)
|
|||
|
|
{
|
|||
|
|
if (avgNumOfNodes % 2 == 0)
|
|||
|
|
{
|
|||
|
|
if (avgNumOfNodes > 1) avgNumOfNodes -= 1;
|
|||
|
|
else avgNumOfNodes += 1;
|
|||
|
|
}
|
|||
|
|
// Change number of elements to an even number
|
|||
|
|
if (recombine && (recombineAlgorithm == GmshAlgorithmRecombineEnum.SimpleFullQuad ||
|
|||
|
|
recombineAlgorithm == GmshAlgorithmRecombineEnum.BlossomFullQuad))
|
|||
|
|
{
|
|||
|
|
if (avgNumOfNodes % 2 == 0) avgNumOfNodes--;
|
|||
|
|
if (avgNumOfNodes < 3) avgNumOfNodes = 3;
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.Mesh.SetTransfiniteCurve(edgeNodeFromGroup.Value.Id, avgNumOfNodes);
|
|||
|
|
edgeIdNumOfNodes[edgeNodeFromGroup.Value.Id] = avgNumOfNodes;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Sweep mesh layers
|
|||
|
|
int count;
|
|||
|
|
if (_gmshData.EdgeIdsBySweepLayer != null)
|
|||
|
|
{
|
|||
|
|
foreach (var layer in _gmshData.EdgeIdsBySweepLayer)
|
|||
|
|
{
|
|||
|
|
sum = 0;
|
|||
|
|
count = 0;
|
|||
|
|
foreach (var sweepEdgeId in layer)
|
|||
|
|
{
|
|||
|
|
if (edgeIdNumOfNodes.TryGetValue(sweepEdgeId, out numOfNodes))
|
|||
|
|
{
|
|||
|
|
sum += numOfNodes;
|
|||
|
|
count++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
avgNumOfNodes = (int)Math.Round((double)sum / count, 0, MidpointRounding.AwayFromZero);
|
|||
|
|
//
|
|||
|
|
foreach (var sweepEdgeId in layer)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.Mesh.SetTransfiniteCurve(sweepEdgeId, avgNumOfNodes);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.Mesh.Clear(); // must clear the mesh
|
|||
|
|
//
|
|||
|
|
foreach (var entry in surfaceIdSurface)
|
|||
|
|
{
|
|||
|
|
surface = entry.Value;
|
|||
|
|
//
|
|||
|
|
if (surface.Transfinite)
|
|||
|
|
{
|
|||
|
|
// "Left", "Right", "AlternateLeft" and "AlternateRight"
|
|||
|
|
if (surface.ThreeSided && transfiniteThreeSided)
|
|||
|
|
Gmsh.Model.Mesh.SetTransfiniteSurface(surface.Id, "AlternateLeft", surface.VertexIds);
|
|||
|
|
else if (surface.FourSided && transfiniteFourSided)
|
|||
|
|
Gmsh.Model.Mesh.SetTransfiniteSurface(surface.Id, "AlternateLeft");
|
|||
|
|
//
|
|||
|
|
if (recombine && surface.Recombine) Gmsh.Model.Mesh.SetRecombine(2, surface.Id);
|
|||
|
|
//
|
|||
|
|
//Gmsh.Model.Mesh.SetSmoothing(2, surface.Id, 100);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
foreach (var entry in volumeIdVolume)
|
|||
|
|
{
|
|||
|
|
if (entry.Value.Transfinite) Gmsh.Model.Mesh.SetTransfiniteVolume(entry.Key);
|
|||
|
|
//Gmsh.Mesh.SetOutwardOrientation(volumeDimTag.Item2);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Background methods
|
|||
|
|
private void GetOccNormalsBackground()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
if (_gmshData.FaceIdNodes == null || _gmshData.FaceIdNodes.Count == 0) return;
|
|||
|
|
//
|
|||
|
|
Tuple<int, int>[] outDimTags;
|
|||
|
|
Gmsh.Model.OCC.ImportShapes(_gmshData.GeometryFileName, out outDimTags, false, "");
|
|||
|
|
//
|
|||
|
|
Synchronize(); // must be here
|
|||
|
|
//
|
|||
|
|
RenumberGmshDataByCoor();
|
|||
|
|
//
|
|||
|
|
int faceTag;
|
|||
|
|
FeNode[] nodes;
|
|||
|
|
double[] normal;
|
|||
|
|
double[] concatenatedCoor;
|
|||
|
|
List<Vec3D> normals;
|
|||
|
|
Dictionary<int, List<Vec3D>> nodeIdNormals = new Dictionary<int, List<Vec3D>>();
|
|||
|
|
//
|
|||
|
|
foreach (var entry in _gmshData.FaceIdNodes)
|
|||
|
|
{
|
|||
|
|
faceTag = entry.Key;
|
|||
|
|
nodes = entry.Value;
|
|||
|
|
//
|
|||
|
|
concatenatedCoor = new double[3 * nodes.Length];
|
|||
|
|
for (int i = 0; i < nodes.Length; i++)
|
|||
|
|
Array.Copy(nodes[i].Coor, 0, concatenatedCoor, 3 * i, 3);
|
|||
|
|
double[] concatenatedParametricCoord;
|
|||
|
|
Gmsh.Model.GetParametrization(2, faceTag, concatenatedCoor, out concatenatedParametricCoord);
|
|||
|
|
double[] concatenatedNormals;
|
|||
|
|
Gmsh.Model.GetNormal(faceTag, concatenatedParametricCoord, out concatenatedNormals);
|
|||
|
|
//
|
|||
|
|
for (int i = 0; i < nodes.Length; i++)
|
|||
|
|
{
|
|||
|
|
normal = new double[3];
|
|||
|
|
Array.Copy(concatenatedNormals, 3 * i, normal, 0, 3);
|
|||
|
|
//
|
|||
|
|
if (nodeIdNormals.TryGetValue(nodes[i].Id, out normals)) normals.Add(new Vec3D(normal));
|
|||
|
|
else nodeIdNormals.Add(nodes[i].Id, new List<Vec3D>() { new Vec3D(normal) });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_gmshData.NodeIdNormals = nodeIdNormals;
|
|||
|
|
_error = null;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_error = ex.Message;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
private void GetElementQualitiesBackground()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
Gmsh.Open(_gmshData.MeshFileName);
|
|||
|
|
//
|
|||
|
|
int[] elementTypes;
|
|||
|
|
IntPtr[][] elementTags;
|
|||
|
|
IntPtr[][] nodeTags;
|
|||
|
|
Gmsh.Model.Mesh.GetElements(out elementTypes, out elementTags, out nodeTags, -1, -1);
|
|||
|
|
// Merge elements of different types
|
|||
|
|
HashSet<IntPtr> allElementTags = new HashSet<IntPtr> { };
|
|||
|
|
for (int i = 0; i < elementTags.Length; i++) allElementTags.UnionWith(elementTags[i]);
|
|||
|
|
// Get quality
|
|||
|
|
double[] qualities;
|
|||
|
|
IntPtr[] elementIntPtr = allElementTags.ToArray();
|
|||
|
|
Gmsh.Model.Mesh.GetElementQualities(elementIntPtr, out qualities, _gmshData.ElementQualityMetric);
|
|||
|
|
// Create result
|
|||
|
|
_gmshData.ElementQuality = new Dictionary<int, double>();
|
|||
|
|
for (int i = 0; i < allElementTags.Count; i++)
|
|||
|
|
{
|
|||
|
|
_gmshData.ElementQuality.Add(elementIntPtr[i].ToInt32(), qualities[i]);
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
_error = null;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_error = ex.Message;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
private void DefeatureBackground()
|
|||
|
|
{
|
|||
|
|
if (_gmshData.SurfaceIds == null || _gmshData.SurfaceIds.Length == 0) return;
|
|||
|
|
//
|
|||
|
|
Tuple<int, int>[] outDimTags;
|
|||
|
|
Gmsh.Model.OCC.ImportShapes(_gmshData.GeometryFileName, out outDimTags, false, "");
|
|||
|
|
//
|
|||
|
|
Synchronize(); // must be here
|
|||
|
|
//
|
|||
|
|
RenumberGmshDataByCoor();
|
|||
|
|
//
|
|||
|
|
int[] volumeTags = new int[] { 1 };
|
|||
|
|
int[] surfaceTags = _gmshData.SurfaceIds;
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.OCC.Defeature(volumeTags, surfaceTags, out outDimTags, true);
|
|||
|
|
//
|
|||
|
|
Synchronize();
|
|||
|
|
//
|
|||
|
|
Gmsh.Write(_gmshData.GeometryFileName);
|
|||
|
|
//
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
private void GetCoordinatesFromParameterizationBackground()
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
Gmsh.Open(_gmshData.GeometryFileName);
|
|||
|
|
//
|
|||
|
|
Tuple<int, int>[] surfaceDimTags;
|
|||
|
|
Gmsh.Model.GetEntities(out surfaceDimTags, 2);
|
|||
|
|
//
|
|||
|
|
int n = 5;
|
|||
|
|
int pointCount;
|
|||
|
|
int count = 0;
|
|||
|
|
int tag;
|
|||
|
|
double[] coor1D;
|
|||
|
|
double[] minBounds;
|
|||
|
|
double[] maxBounds;
|
|||
|
|
double[] parametricCoor1D;
|
|||
|
|
_gmshData.Coor = new double[surfaceDimTags.Length][][];
|
|||
|
|
foreach (var entry in surfaceDimTags)
|
|||
|
|
{
|
|||
|
|
tag = entry.Item2;
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.GetParametrizationBounds(2, tag, out minBounds, out maxBounds);
|
|||
|
|
//
|
|||
|
|
pointCount = 0;
|
|||
|
|
parametricCoor1D = new double[n * n * 2];
|
|||
|
|
//
|
|||
|
|
double du = (maxBounds[0] - minBounds[0]) / (n - 1);
|
|||
|
|
double dv = (maxBounds[1] - minBounds[1]) / (n - 1);
|
|||
|
|
//
|
|||
|
|
for (int u = 0; u < n; u++)
|
|||
|
|
{
|
|||
|
|
for (int v = 0; v < n; v++)
|
|||
|
|
{
|
|||
|
|
parametricCoor1D[pointCount++] = minBounds[0] + du * u;
|
|||
|
|
parametricCoor1D[pointCount++] = minBounds[1] + dv * v;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
Gmsh.Model.GetValue(2, tag, parametricCoor1D, out coor1D);
|
|||
|
|
//
|
|||
|
|
_gmshData.Coor[count] = new double[coor1D.Length / 3][];
|
|||
|
|
for (int i = 0; i < _gmshData.Coor[count].Length; i++)
|
|||
|
|
{
|
|||
|
|
_gmshData.Coor[count][i] = new double[] { coor1D[3 * i], coor1D[3 * i + 1], coor1D[3 * i + 2] };
|
|||
|
|
}
|
|||
|
|
count += 1;
|
|||
|
|
}
|
|||
|
|
_error = null;
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
_error = ex.Message;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Tools
|
|||
|
|
private void RenumberGmshDataByCoor()
|
|||
|
|
{
|
|||
|
|
Dictionary<int, int> netgenVertexIdGmshVertexId = RenumberVertices();
|
|||
|
|
Dictionary<int, int> netgenEdgeIdGmshEdgeId = RenumberEdges(netgenVertexIdGmshVertexId);
|
|||
|
|
Dictionary<int, int> netgenFaceIdGmshFaceId = RenumberFaces(netgenVertexIdGmshVertexId);
|
|||
|
|
|
|||
|
|
// Renumber mesh data
|
|||
|
|
// Vertex mesh size
|
|||
|
|
if (_gmshData.VertexNodeIdMeshSize != null)
|
|||
|
|
{
|
|||
|
|
Dictionary<int, double> vertexIdMeshSize = new Dictionary<int, double>();
|
|||
|
|
foreach (var entry in _gmshData.VertexNodeIdMeshSize)
|
|||
|
|
{
|
|||
|
|
if (netgenVertexIdGmshVertexId.TryGetValue(entry.Key, out int vertexId))
|
|||
|
|
{
|
|||
|
|
vertexIdMeshSize[vertexId] = entry.Value;
|
|||
|
|
}
|
|||
|
|
else if (System.Diagnostics.Debugger.IsAttached)
|
|||
|
|
{
|
|||
|
|
throw new Exception();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_gmshData.VertexNodeIdMeshSize = vertexIdMeshSize;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Edge mesh size
|
|||
|
|
if (_gmshData.EdgeIdNumElements != null)
|
|||
|
|
{
|
|||
|
|
Dictionary<int, int> edgeIdNumElements = new Dictionary<int, int>();
|
|||
|
|
foreach (KeyValuePair<int, int> entry in _gmshData.EdgeIdNumElements)
|
|||
|
|
{
|
|||
|
|
if (netgenEdgeIdGmshEdgeId.TryGetValue(entry.Key, out int edgeId))
|
|||
|
|
{
|
|||
|
|
edgeIdNumElements[edgeId] = entry.Value;
|
|||
|
|
}
|
|||
|
|
else if (System.Diagnostics.Debugger.IsAttached)
|
|||
|
|
{
|
|||
|
|
// Other part
|
|||
|
|
// edgeId = edgeId; 修复2025-8-28 by Luke
|
|||
|
|
throw new Exception();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_gmshData.EdgeIdNumElements = edgeIdNumElements;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Normals
|
|||
|
|
if (_gmshData.FaceIdNodes != null)
|
|||
|
|
{
|
|||
|
|
Dictionary<int, FeNode[]> faceIdNodes = new Dictionary<int, FeNode[]>();
|
|||
|
|
foreach (KeyValuePair<int, FeNode[]> entry in _gmshData.FaceIdNodes)
|
|||
|
|
{
|
|||
|
|
if (netgenFaceIdGmshFaceId.TryGetValue(entry.Key, out int faceId))
|
|||
|
|
{
|
|||
|
|
faceIdNodes[faceId] = entry.Value;
|
|||
|
|
}
|
|||
|
|
else if (System.Diagnostics.Debugger.IsAttached)
|
|||
|
|
{
|
|||
|
|
throw new Exception();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_gmshData.FaceIdNodes = faceIdNodes;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Defeature
|
|||
|
|
if (_gmshData.SurfaceIds != null)
|
|||
|
|
{
|
|||
|
|
List<int> surfaceIdsList = new List<int>();
|
|||
|
|
foreach (int surfaceId in _gmshData.SurfaceIds)
|
|||
|
|
{
|
|||
|
|
if (netgenFaceIdGmshFaceId.TryGetValue(surfaceId, out int faceId))
|
|||
|
|
{
|
|||
|
|
surfaceIdsList.Add(faceId);
|
|||
|
|
}
|
|||
|
|
else if (System.Diagnostics.Debugger.IsAttached)
|
|||
|
|
{
|
|||
|
|
throw new Exception();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_gmshData.SurfaceIds = surfaceIdsList.ToArray();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Sweep data
|
|||
|
|
if (_gmshData.GmshSetupItems != null)
|
|||
|
|
{
|
|||
|
|
foreach (MeshSetupItem setupItem in _gmshData.GmshSetupItems)
|
|||
|
|
{
|
|||
|
|
if (setupItem is SweepMesh sw)
|
|||
|
|
{
|
|||
|
|
for (int i = 0; i < sw.SideSurfaceIds.Length; i++)
|
|||
|
|
{
|
|||
|
|
if (netgenFaceIdGmshFaceId.TryGetValue(sw.SideSurfaceIds[i], out int faceId))
|
|||
|
|
{
|
|||
|
|
sw.SideSurfaceIds[i] = faceId;
|
|||
|
|
}
|
|||
|
|
else if (System.Diagnostics.Debugger.IsAttached)
|
|||
|
|
{
|
|||
|
|
throw new Exception();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
for (int i = 0; i < sw.LayerGroupEdgeIds.Length; i++)
|
|||
|
|
{
|
|||
|
|
for (int j = 0; j < sw.LayerGroupEdgeIds[i].Length; j++)
|
|||
|
|
{
|
|||
|
|
for (int z = 0; z < sw.LayerGroupEdgeIds[i][j].Length; z++)
|
|||
|
|
{
|
|||
|
|
if (netgenEdgeIdGmshEdgeId.TryGetValue(sw.LayerGroupEdgeIds[i][j][z], out int edgeId))
|
|||
|
|
{
|
|||
|
|
sw.LayerGroupEdgeIds[i][j][z] = edgeId;
|
|||
|
|
}
|
|||
|
|
else if (System.Diagnostics.Debugger.IsAttached)
|
|||
|
|
{
|
|||
|
|
throw new Exception();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void PrepareData(GmshSetupItem gmshSetupItem)
|
|||
|
|
{
|
|||
|
|
// Gather sweep edge ids by layer
|
|||
|
|
_gmshData.EdgeIdsBySweepLayer = null;
|
|||
|
|
if (gmshSetupItem is SweepMesh sm)
|
|||
|
|
{
|
|||
|
|
_gmshData.EdgeIdsBySweepLayer = new HashSet<int>[sm.LayerGroupEdgeIds.Length];
|
|||
|
|
for (int i = 0; i < sm.LayerGroupEdgeIds.Length; i++)
|
|||
|
|
{
|
|||
|
|
_gmshData.EdgeIdsBySweepLayer[i] = new HashSet<int>();
|
|||
|
|
for (int j = 0; j < sm.LayerGroupEdgeIds[i].Length; j++)
|
|||
|
|
{
|
|||
|
|
_gmshData.EdgeIdsBySweepLayer[i].UnionWith(sm.LayerGroupEdgeIds[i][j]);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Dictionary<int, int> RenumberVertices()
|
|||
|
|
{
|
|||
|
|
// Vertices
|
|||
|
|
Gmsh.Model.GetEntities(out Tuple<int, int>[] pointDimTags, 0);
|
|||
|
|
|
|||
|
|
Dictionary<int, double[]> pointIdCoor = new Dictionary<int, double[]>();
|
|||
|
|
foreach (Tuple<int, int> item in pointDimTags)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.GetValue(item.Item1, item.Item2, new double[3], out double[] coor);
|
|||
|
|
pointIdCoor.Add(item.Item2, coor);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Dictionary<int, int> netgenVertexIdGmshVertexId = new Dictionary<int, int>();
|
|||
|
|
foreach (KeyValuePair<int, FeNode> netGenEntry in _gmshData.VertexNodes)
|
|||
|
|
{
|
|||
|
|
int minId = -1;
|
|||
|
|
double minDistance = double.MaxValue;
|
|||
|
|
double[] coorN = netGenEntry.Value.Coor;
|
|||
|
|
foreach (KeyValuePair<int, double[]> gmshEntry in pointIdCoor)
|
|||
|
|
{
|
|||
|
|
double[] coorG = gmshEntry.Value;
|
|||
|
|
|
|||
|
|
double dx = Math.Abs(coorN[0] - coorG[0]);
|
|||
|
|
if (dx <= minDistance)
|
|||
|
|
{
|
|||
|
|
double dy = Math.Abs(coorN[1] - coorG[1]);
|
|||
|
|
if (dy <= minDistance)
|
|||
|
|
{
|
|||
|
|
double dz = Math.Abs(coorN[2] - coorG[2]);
|
|||
|
|
if (dz <= minDistance)
|
|||
|
|
{
|
|||
|
|
minDistance = Math.Pow(dx, 2) + Math.Pow(dy, 2) + Math.Pow(dz, 2);
|
|||
|
|
minId = gmshEntry.Key;
|
|||
|
|
if (minDistance == 0) break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// 存放netgen节点和gmsh节点的对应关系
|
|||
|
|
netgenVertexIdGmshVertexId.Add(netGenEntry.Key, minId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return netgenVertexIdGmshVertexId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 对边进行重新排序
|
|||
|
|
private Dictionary<int, int> RenumberEdges(Dictionary<int, int> netgenVertexIdGmshVertexId)
|
|||
|
|
{
|
|||
|
|
// Edges
|
|||
|
|
int[] vertexIds;
|
|||
|
|
CompareIntArray comparer = new CompareIntArray();
|
|||
|
|
List<GmshIdLocation> edgeIdDataList;
|
|||
|
|
Dictionary<int[], List<GmshIdLocation>> edgeVertexNodeIdsEdgeId = new Dictionary<int[], List<GmshIdLocation>>(comparer);
|
|||
|
|
// Renumber edge vertex node ids as keys
|
|||
|
|
foreach (var entry in _gmshData.EdgeVertexNodeIdsEdgeId)
|
|||
|
|
{
|
|||
|
|
vertexIds = new int[entry.Key.Length];
|
|||
|
|
for (int i = 0; i < vertexIds.Length; i++)
|
|||
|
|
{
|
|||
|
|
// gmsh vertex id
|
|||
|
|
vertexIds[i] = netgenVertexIdGmshVertexId[entry.Key[i]];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Array.Sort(vertexIds);
|
|||
|
|
|
|||
|
|
if (edgeVertexNodeIdsEdgeId.TryGetValue(vertexIds, out edgeIdDataList))
|
|||
|
|
{
|
|||
|
|
edgeIdDataList.AddRange(entry.Value);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// create a copy to ba able to AddRange later
|
|||
|
|
edgeVertexNodeIdsEdgeId.Add(vertexIds, entry.Value.ToList());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_gmshData.EdgeVertexNodeIdsEdgeId = edgeVertexNodeIdsEdgeId;
|
|||
|
|
|
|||
|
|
// Edges - prepare a map of netgen edge id -> gmsh edge id
|
|||
|
|
// 先读出所有边的Tag
|
|||
|
|
Gmsh.Model.GetEntities(out Tuple<int, int>[] dimTags, 1);
|
|||
|
|
|
|||
|
|
// Prepare Gmsh edge data by vertex ids
|
|||
|
|
Dictionary<int[], List<GmshIdLocation>> gmshEdgeVertexNodeIdsEdgeId = new Dictionary<int[], List<GmshIdLocation>>(comparer);
|
|||
|
|
BoundingBox bb = new BoundingBox();
|
|||
|
|
Vec3D xyz;
|
|||
|
|
foreach (var entry in dimTags)
|
|||
|
|
{
|
|||
|
|
// 查询边上所有节点
|
|||
|
|
var edgeId = entry.Item2;
|
|||
|
|
Gmsh.Model.GetAdjacencies(1, edgeId, out _, out vertexIds);
|
|||
|
|
|
|||
|
|
// 对进行排序
|
|||
|
|
Array.Sort(vertexIds);
|
|||
|
|
|
|||
|
|
// 得到当前边的位置信息
|
|||
|
|
double x;
|
|||
|
|
double y;
|
|||
|
|
double z;
|
|||
|
|
if (_isOCC)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.OCC.GetCenterOfMass(1, edgeId, out x, out y, out z);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.GetBoundingBox(1, edgeId, out bb.MinX, out bb.MinY, out bb.MinZ,
|
|||
|
|
out bb.MaxX, out bb.MaxY, out bb.MaxZ);
|
|||
|
|
var center = bb.GetCenter();
|
|||
|
|
x = center[0];
|
|||
|
|
y = center[1];
|
|||
|
|
z = center[2];
|
|||
|
|
}
|
|||
|
|
xyz = new Vec3D(x, y, z);
|
|||
|
|
|
|||
|
|
//
|
|||
|
|
var edgeIdData = new GmshIdLocation { Id = edgeId, Location = xyz.Coor };
|
|||
|
|
|
|||
|
|
if (gmshEdgeVertexNodeIdsEdgeId.TryGetValue(vertexIds, out edgeIdDataList))
|
|||
|
|
{
|
|||
|
|
edgeIdDataList.Add(edgeIdData);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// create a copy to ba able to AddRange later
|
|||
|
|
gmshEdgeVertexNodeIdsEdgeId.Add(vertexIds, new List<GmshIdLocation> { edgeIdData });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Renumber edge ids
|
|||
|
|
int gmshEdgeId = -1;
|
|||
|
|
Dictionary<int, int> netgenEdgeIdGmshEdgeId = new Dictionary<int, int>();
|
|||
|
|
foreach (KeyValuePair<int[], List<GmshIdLocation>> entry in _gmshData.EdgeVertexNodeIdsEdgeId)
|
|||
|
|
{
|
|||
|
|
vertexIds = entry.Key;
|
|||
|
|
//
|
|||
|
|
foreach (GmshIdLocation edgeDataEntry in entry.Value)
|
|||
|
|
{
|
|||
|
|
var netgenEdgeId = edgeDataEntry.Id;
|
|||
|
|
xyz = new Vec3D(edgeDataEntry.Location);
|
|||
|
|
|
|||
|
|
//
|
|||
|
|
if (gmshEdgeVertexNodeIdsEdgeId.TryGetValue(vertexIds, out var idLocationList))
|
|||
|
|
{
|
|||
|
|
if (idLocationList.Count == 1)
|
|||
|
|
{
|
|||
|
|
gmshEdgeId = idLocationList[0].Id;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
var min = double.MaxValue;
|
|||
|
|
foreach (GmshIdLocation idLocation in idLocationList)
|
|||
|
|
{
|
|||
|
|
var cog = new Vec3D(idLocation.Location);
|
|||
|
|
var d2 = (cog - xyz).Len2;
|
|||
|
|
if (d2 < min)
|
|||
|
|
{
|
|||
|
|
min = d2;
|
|||
|
|
gmshEdgeId = idLocation.Id;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
netgenEdgeIdGmshEdgeId[netgenEdgeId] = gmshEdgeId;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return netgenEdgeIdGmshEdgeId;
|
|||
|
|
}
|
|||
|
|
private Dictionary<int, int> RenumberFaces(Dictionary<int, int> netgenVertexIdGmshVertexId)
|
|||
|
|
{
|
|||
|
|
// Faces
|
|||
|
|
int[] vertexIds;
|
|||
|
|
CompareIntArray comparer = new CompareIntArray();
|
|||
|
|
List<GmshIdLocation> faceIdDataList;
|
|||
|
|
Dictionary<int[], List<GmshIdLocation>> faceVertexNodeIdsFaceId = new Dictionary<int[], List<GmshIdLocation>>(comparer);
|
|||
|
|
// Renumber face vertex node ids as keys
|
|||
|
|
foreach (KeyValuePair<int[], List<GmshIdLocation>> entry in _gmshData.FaceVertexNodeIdsFaceId)
|
|||
|
|
{
|
|||
|
|
vertexIds = new int[entry.Key.Length];
|
|||
|
|
for (int i = 0; i < vertexIds.Length; i++)
|
|||
|
|
{
|
|||
|
|
vertexIds[i] = netgenVertexIdGmshVertexId[entry.Key[i]];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
Array.Sort(vertexIds);
|
|||
|
|
|
|||
|
|
if (faceVertexNodeIdsFaceId.TryGetValue(vertexIds, out faceIdDataList))
|
|||
|
|
{
|
|||
|
|
faceIdDataList.AddRange(entry.Value);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// create a copy to ba able to AddRange later
|
|||
|
|
faceVertexNodeIdsFaceId.Add(vertexIds, entry.Value.ToList());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_gmshData.FaceVertexNodeIdsFaceId = faceVertexNodeIdsFaceId;
|
|||
|
|
|
|||
|
|
// Vertices - prepare a map of netgen edge id -> gmsh edge id
|
|||
|
|
|
|||
|
|
Gmsh.Model.GetEntities(out Tuple<int, int>[] dimTags, 2);
|
|||
|
|
|
|||
|
|
BoundingBox bb = new BoundingBox();
|
|||
|
|
Vec3D xyz;
|
|||
|
|
// Prepare Gmsh edge data by vertex ids
|
|||
|
|
Dictionary<int[], List<GmshIdLocation>> gmshFaceVertexNodeIdsFaceId = new Dictionary<int[], List<GmshIdLocation>>(comparer);
|
|||
|
|
foreach (Tuple<int, int> entry in dimTags)
|
|||
|
|
{
|
|||
|
|
int faceId = entry.Item2;
|
|||
|
|
|
|||
|
|
Gmsh.Model.GetAdjacencies(2, faceId, out _, out int[] edgeIds);
|
|||
|
|
|
|||
|
|
HashSet<int> faceVertexIds = new HashSet<int>();
|
|||
|
|
// ReSharper disable once ForCanBeConvertedToForeach
|
|||
|
|
for (int i = 0; i < edgeIds.Length; i++)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.GetAdjacencies(1, edgeIds[i], out _, out vertexIds);
|
|||
|
|
faceVertexIds.UnionWith(vertexIds);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
vertexIds = faceVertexIds.ToArray();
|
|||
|
|
|
|||
|
|
Array.Sort(vertexIds);
|
|||
|
|
|
|||
|
|
//
|
|||
|
|
double x;
|
|||
|
|
double y;
|
|||
|
|
double z;
|
|||
|
|
if (_isOCC)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.OCC.GetCenterOfMass(2, faceId, out x, out y, out z);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.GetBoundingBox(2, faceId, out bb.MinX, out bb.MinY, out bb.MinZ,
|
|||
|
|
out bb.MaxX, out bb.MaxY, out bb.MaxZ);
|
|||
|
|
double[] center = bb.GetCenter();
|
|||
|
|
x = center[0];
|
|||
|
|
y = center[1];
|
|||
|
|
z = center[2];
|
|||
|
|
}
|
|||
|
|
xyz = new Vec3D(x, y, z);
|
|||
|
|
|
|||
|
|
//
|
|||
|
|
double size;
|
|||
|
|
if (_isOCC)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.OCC.GetMass(2, faceId, out size);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
size = -1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//
|
|||
|
|
GmshIdLocation faceIdData = new GmshIdLocation
|
|||
|
|
{
|
|||
|
|
Id = faceId,
|
|||
|
|
Size = size,
|
|||
|
|
Location = xyz.Coor
|
|||
|
|
};
|
|||
|
|
//
|
|||
|
|
if (gmshFaceVertexNodeIdsFaceId.TryGetValue(vertexIds, out faceIdDataList))
|
|||
|
|
{
|
|||
|
|
faceIdDataList.Add(faceIdData);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
gmshFaceVertexNodeIdsFaceId.Add(vertexIds, new List<GmshIdLocation> { faceIdData });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Renumber face ids based on the most simillar of two criteria
|
|||
|
|
Dictionary<int, int> netgenFaceIdGmshFaceId = new Dictionary<int, int>();
|
|||
|
|
foreach (KeyValuePair<int[], List<GmshIdLocation>> entry in _gmshData.FaceVertexNodeIdsFaceId)
|
|||
|
|
{
|
|||
|
|
vertexIds = entry.Key;
|
|||
|
|
//
|
|||
|
|
foreach (GmshIdLocation faceDataEntry in entry.Value)
|
|||
|
|
{
|
|||
|
|
int netgenFaceId = faceDataEntry.Id;
|
|||
|
|
xyz = new Vec3D(faceDataEntry.Location);
|
|||
|
|
//
|
|||
|
|
if (gmshFaceVertexNodeIdsFaceId.TryGetValue(vertexIds, out List<GmshIdLocation> idLocationList))
|
|||
|
|
{
|
|||
|
|
int gmshFaceId = -1;
|
|||
|
|
if (idLocationList.Count() == 1)
|
|||
|
|
{
|
|||
|
|
gmshFaceId = idLocationList[0].Id;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
int[] locationIdDiff = new int[idLocationList.Count()];
|
|||
|
|
int[] sizeIdDiff = new int[idLocationList.Count()];
|
|||
|
|
double[] locationDiff = new double[idLocationList.Count()];
|
|||
|
|
double[] sizeDiff = new double[idLocationList.Count()];
|
|||
|
|
// Collect all differences
|
|||
|
|
int count = 0;
|
|||
|
|
foreach (GmshIdLocation idLocation in idLocationList)
|
|||
|
|
{
|
|||
|
|
Vec3D cog = new Vec3D(idLocation.Location);
|
|||
|
|
double d2 = (cog - xyz).Len2;
|
|||
|
|
locationIdDiff[count] = idLocation.Id;
|
|||
|
|
locationDiff[count] = d2;
|
|||
|
|
//
|
|||
|
|
d2 = Math.Abs(faceDataEntry.Size - idLocation.Size);
|
|||
|
|
sizeIdDiff[count] = idLocation.Id;
|
|||
|
|
sizeDiff[count] = d2;
|
|||
|
|
//
|
|||
|
|
count++;
|
|||
|
|
}
|
|||
|
|
// Sort differences
|
|||
|
|
Array.Sort(locationDiff, locationIdDiff);
|
|||
|
|
Array.Sort(sizeDiff, sizeIdDiff);
|
|||
|
|
// If different surfaces are found select by the smallest normalized criteria
|
|||
|
|
if (locationIdDiff[0] != sizeIdDiff[0])
|
|||
|
|
{
|
|||
|
|
// Normalize differences
|
|||
|
|
for (int i = 0; i < locationDiff.Length; i++)
|
|||
|
|
{
|
|||
|
|
locationDiff[i] /= locationDiff[locationDiff.Length - 1];
|
|||
|
|
sizeDiff[i] /= sizeDiff[locationIdDiff.Length - 1];
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
if (locationDiff[0] < sizeDiff[0])
|
|||
|
|
{
|
|||
|
|
gmshFaceId = locationIdDiff[0];
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
gmshFaceId = sizeIdDiff[0];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
gmshFaceId = locationIdDiff[0];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
netgenFaceIdGmshFaceId[netgenFaceId] = gmshFaceId;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return netgenFaceIdGmshFaceId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void Synchronize()
|
|||
|
|
{
|
|||
|
|
if (_isOCC)
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.OCC.Synchronize();
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
Gmsh.Model.Geo.Synchronize();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void WriteLog()
|
|||
|
|
{
|
|||
|
|
if (Gmsh.IsInitialized() == 1)
|
|||
|
|
{
|
|||
|
|
string[] loggerLines = Gmsh.Logger.Get();
|
|||
|
|
if (loggerLines != null && loggerLines.Length > 0)
|
|||
|
|
{
|
|||
|
|
StringBuilder sb = new StringBuilder();
|
|||
|
|
for (int i = _currentLogLine; i < loggerLines.Length; i++)
|
|||
|
|
{
|
|||
|
|
sb.AppendLine(loggerLines[i]);
|
|||
|
|
}
|
|||
|
|
//
|
|||
|
|
_currentLogLine = loggerLines.Length;
|
|||
|
|
//
|
|||
|
|
_writeOutput?.Invoke(sb.ToString());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|