You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Gmsh处理STL转体网格时拓扑错误及分区循环问题求解

问题描述

我尝试将ImageJ/Fiji分割导出的3D STL表面模型,通过Gmsh官方教程t13的典型流程,用Gmsh Python API转换为体网格,代码如下:

n_file = "Birch_745_746-1_Extract3.stl"

gmsh.initialize()
gmsh.clear()
gmsh.model.add(n_file[:-4])
gmsh.merge(n_file)

angle = 40 
forceParametrizablePatches = True 
includeBoundary = True
curveAngle = 180
gmsh.model.mesh.classifySurfaces(
            angle * math.pi / 180.,
            includeBoundary,
            forceParametrizablePatches,
            curveAngle * math.pi / 180.
        )
    

gmsh.model.mesh.createGeometry()

s = gmsh.model.getEntities(2)
l = gmsh.model.geo.addSurfaceLoop([e[1] for e in s])
gmsh.model.geo.addVolume([l])

gmsh.model.geo.synchronize()

gmsh.model.mesh.generate(3)

gmsh.write(f'{n_file[:-4]}.msh')

gmsh.finalize()

报错情况

  • 当设置forceParametrizablePatches = True时,createGeometry方法报错:
Info    : [ 10%] Discrete surface 28 is planar, simplifying parametrization
Error   : Wrong topology of boundary mesh for parametrization
Exception: Wrong topology of boundary mesh for parametrization
  • 当设置forceParametrizablePatches = False时,出现大量循环警告且程序无法结束:
Warning : Partitioning face 1 with 2 triangles that all have the same partition - changing tolerance to 30.55
...
Warning : Tolerance too large - aborting partitioning
Info    : Level ... Poincaré characteristic ... not 0

该STL文件来自ImageJ/Fiji的BoneJ插件,是断层扫描数据的一小部分。

已尝试的解决方法

  • 使用ImageJ/Fiji及MeshLab的各类插件清理STL
  • 使用Python的trimesh库清理模型
  • 调整angle参数的多个取值
  • 改用gmsh.model.mesh.createTopology()替代gmsh.model.mesh.createGeometry(),但出现错误:
gmsh.model.mesh.generate(3)
    ~~~~~~~~~~~~~~~~~~~~~~~~^^^
  File "\gmsh.py", line 2060, in generate
    raise Exception(logger.getLastError())
Exception: Invalid boundary mesh (overlapping facets) on surface 1 surface 1

核心疑问

  1. 将表面网格转为几何模型时,这些“Wrong topology”/“changing tolerance”循环问题的根本原因是什么?
  2. 如何将该STL文件转换为可用于有限元仿真的3D体网格?

问题分析与解决方法

根本原因

  1. 拓扑缺陷:断层扫描导出的STL模型(尤其是BoneJ生成的)常存在多种原生问题:
    • 非流形边/顶点:多个面共享同一条边或顶点,导致Gmsh无法识别闭合的表面边界
    • 重叠/共面面片:模型中存在位置近乎重合的三角形面片,触发Gmsh分区容错机制反复调整阈值,陷入死循环
    • 非闭合表面:模型存在微小缺口,导致Poincaré特征值不为0,无法形成闭合体
  2. 参数化冲突:当forceParametrizablePatches = True时,Gmsh强制将所有离散曲面转换为可参数化的NURBS曲面,但部分拓扑有缺陷的表面无法满足参数化的拓扑要求(比如边界环不闭合、存在分支),因此触发报错。

解决步骤

步骤1:彻底修复STL拓扑缺陷

常规工具的默认清理可能不够彻底,需针对性处理:

  • MeshLab流程
    1. 导入STL后,执行Filters > Cleaning and Repairing > Remove Duplicate Faces
    2. 执行Filters > Cleaning and Repairing > Remove Duplicate Vertices(设置容差为1e-6左右)
    3. 执行Filters > Cleaning and Repairing > Fill Holes(选择填充较大孔洞并设置面片质量)
    4. 执行Filters > Cleaning and Repairing > Remove Non Manifold Edges
  • Blender备选流程
    1. 导入STL进入编辑模式,全选顶点
    2. 执行Mesh > Clean Up > Merge by Distance(调整容差匹配模型尺寸)
    3. 执行Mesh > Clean Up > Delete Loose Geometry
    4. 执行Mesh > Faces > Fill Holes

步骤2:调整Gmsh参数适配修复后的模型

使用优化后的Gmsh代码:

import math
import gmsh

n_file = "Birch_745_746-1_Extract3_repaired.stl"

gmsh.initialize()
gmsh.clear()
model = gmsh.model.add(n_file[:-4])

# 设置网格容差,匹配修复后的模型精度
gmsh.option.setNumber("Mesh.Tolerance", 1e-6)
gmsh.merge(n_file)

# 调整曲面分类参数,减小角度避免误判
angle = 30
forceParametrizablePatches = False  # 禁用强制参数化,保留离散曲面
includeBoundary = True
curveAngle = 180

gmsh.model.mesh.classifySurfaces(
    angle * math.pi / 180.,
    includeBoundary,
    forceParametrizablePatches,
    curveAngle * math.pi / 180.
)

# 使用createTopology跳过参数化步骤,直接构建拓扑
gmsh.model.mesh.createTopology()

# 创建表面环与体
s = gmsh.model.getEntities(2)
if len(s) == 0:
    raise Exception("修复后未检测到有效曲面")

surface_loop = gmsh.model.geo.addSurfaceLoop([e[1] for e in s])
volume = gmsh.model.geo.addVolume([surface_loop])
gmsh.model.geo.synchronize()

# 设置体网格参数,使用HXT算法适配复杂边界
gmsh.option.setNumber("Mesh.CharacteristicLengthMin", 0.1)  # 根据模型尺寸调整
gmsh.option.setNumber("Mesh.CharacteristicLengthMax", 0.5)
gmsh.option.setNumber("Mesh.Algorithm3D", 10)

# 生成并检查网格质量
gmsh.model.mesh.generate(3)
gmsh.option.setNumber("Mesh.CheckQuality", 1)
gmsh.model.mesh.check()

gmsh.write(f'{n_file[:-4]}.msh')
gmsh.finalize()

步骤3:备选方案:直接基于离散表面生成体网格

若上述方法仍失败,可跳过几何重建,直接从表面网格生成体:

# 在merge文件后添加以下配置
gmsh.model.mesh.createMesh(2)  # 先生成表面网格
gmsh.option.setNumber("Mesh.GenerateVolumeMeshFromSurfaceMesh", 1)  # 启用直接体网格生成
gmsh.model.mesh.generate(3)

此方法无需重建几何拓扑,适合拓扑复杂的扫描类模型。


内容的提问来源于stack exchange,提问作者nicpi

火山引擎 最新活动