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 _writeOutput; private bool _isOCC; private string _error; private Thread _thread; private int _currentLogLine; // Properties public GmshData GmshData => _gmshData; // 构造函数 public GmshAPI(GmshData gmshData, Action 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[] 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[] dimTags = new Tuple[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[] 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[] dimTags = new Tuple[1]; foreach (var entry in _gmshData.VertexNodeIdMeshSize) { dimTags[0] = new Tuple(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 edgeIdNumberOfElements = new Dictionary(); 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[] dimTags = new Tuple[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[] dimTags = new Tuple[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 allExtrudeSurfaceIds = new HashSet(); bool recombine = gmshSetupItem.AlgorithmRecombine != GmshAlgorithmRecombineEnum.None; Tuple[] extrudeDimTags = new Tuple[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(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[] allDimTags = Gmsh.Model.OCC.GetEntities(2); List> toRemoveDimTags = new List>() { new Tuple(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[] 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[] { new Tuple(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 sourceSurfaceIds = new HashSet(); 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 surfaceIdEdgeIds; Dictionary surfaceIdVertexIds; GetSurfaceItems(out surfaceIdEdgeIds, out surfaceIdVertexIds); // Get side surfaces HashSet sideSurfaceIds = new HashSet(sweepMesh.SideSurfaceIds); HashSet targetSurfaceIds = new HashSet(); // 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> toRemoveDimTags = new List>() { new Tuple(3, 1) }; // volume foreach (var targetSurfaceId in targetSurfaceIds) toRemoveDimTags.Add(new Tuple(2, targetSurfaceId)); // Gmsh.Model.Mesh.Clear(toRemoveDimTags.ToArray()); // Optimize first order if (sweepMesh.OptimizeFirstOrderShell != GmshOptimizeFirstOrderShellEnum.None) { Tuple[] dimTags = new Tuple[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>(); foreach (var sideSurfaceId in sideSurfaceIds) toRemoveDimTags.Add(new Tuple(2, sideSurfaceId)); // Gmsh.Model.Mesh.Clear(toRemoveDimTags.ToArray()); } else { SweepMethods.CreateSweepMesh(sourceSurfaceIds, sideSurfaceIds, targetSurfaceIds, sweepMesh.NumberOfLayerSmoothSteps, sweepMesh.NumberOfGlobalSmoothSteps, surfaceIdEdgeIds, surfaceIdVertexIds); } } // public bool CheckMeshVolume(Tuple[] 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 ExportCADPartGeometryToDefaultFile) { int surfaceId; string brepFileName = ExportCADPartGeometryToDefaultFile(part); // Initialize Gmsh.Initialize(); Gmsh.Model.Add("Brep_model"); // Import Tuple[] outDimTags; Gmsh.Model.OCC.ImportShapes(brepFileName, out outDimTags, false, ""); Gmsh.Model.OCC.Synchronize(); // must be here // Get surfaces to extrude Tuple[] dimTags = new Tuple[extrudeMesh.CreationIds.Length]; for (int i = 0; i < extrudeMesh.CreationIds.Length; i++) { surfaceId = FeMesh.GetItemIdFromGeometryId(extrudeMesh.CreationIds[i]) + 1; dimTags[i] = new Tuple(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 surfaceIdEdgeIds, out Dictionary surfaceIdVertexIds) { Tuple[] surfaceDimTags = Gmsh.Model.OCC.GetEntities(2); // int[] edgeIds; int[] vertexIds; HashSet allVertexIds; surfaceIdEdgeIds = new Dictionary(); surfaceIdVertexIds = new Dictionary(); // foreach (var surfaceDimTag in surfaceDimTags) { Gmsh.Model.GetAdjacencies(2, surfaceDimTag.Item2, out _, out edgeIds); surfaceIdEdgeIds.Add(surfaceDimTag.Item2, edgeIds); // allVertexIds = new HashSet(); 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 surfaceVertexIds = new HashSet(); //bool invertEdge; int edgeDim = 1; int edgeId; int surfaceDim = 2; int surfaceId; int volumeId; double edgeLength; double[] edgeLengths; // GmshEdge edge; GmshEdge existingEdge; Dictionary edgeIdEdge = new Dictionary(); GmshSurface surface; Dictionary surfaceIdSurface = new Dictionary(); GmshVolume volume; GmshVolume existingVolume; Dictionary volumeIdVolume = new Dictionary(); // Tuple[] vertexDimTags; Tuple[] edgeDimTags; Tuple[] surfaceDimTags; Tuple[] 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[] { new Tuple(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 connectingEdgeIds = new HashSet(); 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 edgeNode; Graph edgeGraph = new Graph(); Dictionary> edgeIdNodeEdge = new Dictionary>(); // 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(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> edgeGroups = edgeGraph.GetConnectedSubgraphs(); // int min; int max; int sum; int maxByRefinement; int numOfElements; int numOfNodes; int avgNumOfNodes; HashSet groupEdgeIds; IntPtr[] nodeTagsIntPtr; double[] coor; // Create edge mesh to get the number of nodes for each edge Gmsh.Model.Mesh.Generate(1); // Dictionary edgeIdNumOfNodes = new Dictionary(); 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(); // 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[] outDimTags; Gmsh.Model.OCC.ImportShapes(_gmshData.GeometryFileName, out outDimTags, false, ""); // Synchronize(); // must be here // RenumberGmshDataByCoor(); // int faceTag; FeNode[] nodes; double[] normal; double[] concatenatedCoor; List normals; Dictionary> nodeIdNormals = new Dictionary>(); // 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() { 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 allElementTags = new HashSet { }; 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(); 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[] 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[] 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 netgenVertexIdGmshVertexId = RenumberVertices(); Dictionary netgenEdgeIdGmshEdgeId = RenumberEdges(netgenVertexIdGmshVertexId); Dictionary netgenFaceIdGmshFaceId = RenumberFaces(netgenVertexIdGmshVertexId); // Renumber mesh data // Vertex mesh size if (_gmshData.VertexNodeIdMeshSize != null) { Dictionary vertexIdMeshSize = new Dictionary(); 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 edgeIdNumElements = new Dictionary(); foreach (KeyValuePair 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 faceIdNodes = new Dictionary(); foreach (KeyValuePair 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 surfaceIdsList = new List(); 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[sm.LayerGroupEdgeIds.Length]; for (int i = 0; i < sm.LayerGroupEdgeIds.Length; i++) { _gmshData.EdgeIdsBySweepLayer[i] = new HashSet(); for (int j = 0; j < sm.LayerGroupEdgeIds[i].Length; j++) { _gmshData.EdgeIdsBySweepLayer[i].UnionWith(sm.LayerGroupEdgeIds[i][j]); } } } } private Dictionary RenumberVertices() { // Vertices Gmsh.Model.GetEntities(out Tuple[] pointDimTags, 0); Dictionary pointIdCoor = new Dictionary(); foreach (Tuple item in pointDimTags) { Gmsh.Model.GetValue(item.Item1, item.Item2, new double[3], out double[] coor); pointIdCoor.Add(item.Item2, coor); } Dictionary netgenVertexIdGmshVertexId = new Dictionary(); foreach (KeyValuePair netGenEntry in _gmshData.VertexNodes) { int minId = -1; double minDistance = double.MaxValue; double[] coorN = netGenEntry.Value.Coor; foreach (KeyValuePair 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 RenumberEdges(Dictionary netgenVertexIdGmshVertexId) { // Edges int[] vertexIds; CompareIntArray comparer = new CompareIntArray(); List edgeIdDataList; Dictionary> edgeVertexNodeIdsEdgeId = new Dictionary>(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[] dimTags, 1); // Prepare Gmsh edge data by vertex ids Dictionary> gmshEdgeVertexNodeIdsEdgeId = new Dictionary>(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 { edgeIdData }); } } // Renumber edge ids int gmshEdgeId = -1; Dictionary netgenEdgeIdGmshEdgeId = new Dictionary(); foreach (KeyValuePair> 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 RenumberFaces(Dictionary netgenVertexIdGmshVertexId) { // Faces int[] vertexIds; CompareIntArray comparer = new CompareIntArray(); List faceIdDataList; Dictionary> faceVertexNodeIdsFaceId = new Dictionary>(comparer); // Renumber face vertex node ids as keys foreach (KeyValuePair> 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[] dimTags, 2); BoundingBox bb = new BoundingBox(); Vec3D xyz; // Prepare Gmsh edge data by vertex ids Dictionary> gmshFaceVertexNodeIdsFaceId = new Dictionary>(comparer); foreach (Tuple entry in dimTags) { int faceId = entry.Item2; Gmsh.Model.GetAdjacencies(2, faceId, out _, out int[] edgeIds); HashSet faceVertexIds = new HashSet(); // 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 { faceIdData }); } } // Renumber face ids based on the most simillar of two criteria Dictionary netgenFaceIdGmshFaceId = new Dictionary(); foreach (KeyValuePair> 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 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()); } } } } }