Files
wg_cpso/CaeMesh/Meshing/Gmsh/GmshAPI.cs

1726 lines
72 KiB
C#
Raw Normal View History

2026-03-25 18:20:24 +08:00
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());
}
}
}
}
}