You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Delaunay三角剖分中识别删除零内角单元(保留节点)

问题描述

我用Python代码生成三角形网格,但生成的网格文件里存在含零内角的单元,这类单元无法用于后续计算。请问如何在已创建的Delaunay三角剖分中识别并删除这些单元,同时保留原有节点?

CSV文件结构:每行存储节点的X、Y坐标,部分文件首行可能是无效的[0,0]值。
问题网格情况:存在三点共线、内角为0的三角形单元。

原代码:

import numpy as np
import matplotlib.pyplot as plt
import pyvista as pv
import os
from scipy.spatial import Delaunay

path = r"C:\Users\\"
filename = "old_mesh.csv"
datos = np.genfromtxt(path + filename, delimiter=',')

# delete X,Y first line
if np.array_equal(datos[0], np.array([0, 0])):
    datos = np.delete(datos, 0, axis=0)

#verifiy if there are NaN values
if np.isnan(datos).any():
    print("El archivo CSV contiene valores faltantes o NaN")
    # elimina las filas con valores faltantes o NaN
    datos = datos[~np.isnan(datos).any(axis=1)]

# creation of the mesh based in the nodes

tri = Delaunay(datos)

# vtu file creation 
with open(path + os.path.basename(filename)[0:-4] + '_outputmesh.vtu', "w") as f:
    f.write('<?xml version="1.0"?>\n')
    f.write('<VTKFile type="UnstructuredGrid" version="1" byte_order="LittleEndian" header_type="UInt64">\n')
    f.write('\t<UnstructuredGrid>\n')
    f.write('\t	<Piece NumberOfPoints="{}" NumberOfCells="{}">\n'.format(len(datos), len(tri.simplices)))
    f.write('\t		<Points>\n')
    f.write('\t			<DataArray type="Float64" Name="Points" NumberOfComponents="3" format="ascii">\n')
    for i in range(len(datos)):
        f.write('\t				{} {} {}\n'.format(datos[i][0], datos[i][1], 0))
    f.write('\t			</DataArray>\n')
    f.write('\t		</Points>\n')
    f.write('\t		<Cells>\n')
    f.write('\t			<DataArray type="Int64" Name="connectivity" NumberOfComponents="1" format="ascii">\n')
    for i in range(len(tri.simplices)):
        f.write('\t				{} {} {}\n'.format(tri.simplices[i][0], tri.simplices[i][1], tri.simplices[i][2]))
    f.write('\t			</DataArray>\n')
    f.write('\t			<DataArray type="Int64" Name="offsets" NumberOfComponents="1" format="ascii">\n')
    for i in range(len(tri.simplices)):
        f.write('\t				{}\n'.format((i+1)*3))
    f.write('\t			</DataArray>\n')
    f.write('\t			<DataArray type="UInt8" Name="types" NumberOfComponents="1" format="ascii">\n')
    for i in range(len(tri.simplices)):
        f.write('\t				5\n')
    f.write('\t			</DataArray>\n')
    f.write('\t		</Cells>\n')
    f.write('\t	</Piece>\n')
    f.write('\t</UnstructuredGrid>\n')
    f.write('</VTKFile>')
解决方案

核心思路是计算每个三角形的三个内角,筛选出所有内角都大于极小阈值(避免浮点精度误差)的单元,剔除含零或接近零内角的无效单元,同时保留全部原始节点。

1. 实现逻辑

  • 编写函数计算单个三角形的三个内角,利用向量点积公式转换为角度值
  • 遍历所有Delaunay剖分生成的单元,筛选出内角均大于阈值的有效单元
  • 修改VTU文件生成逻辑,仅写入有效单元

2. 修改后的完整代码

import numpy as np
import os
from scipy.spatial import Delaunay

def calculate_triangle_angles(points, simplex):
    """计算三角形三个内角(单位:度)"""
    # 获取三个顶点坐标
    p0, p1, p2 = points[simplex]
    
    # 计算边向量
    v0 = p1 - p0
    v1 = p2 - p0
    v2 = p0 - p1
    v3 = p2 - p1
    
    # 计算向量长度,避免三点共线时除以零
    len_v0 = np.linalg.norm(v0)
    len_v1 = np.linalg.norm(v1)
    len_v2 = np.linalg.norm(v2)
    len_v3 = np.linalg.norm(v3)
    
    if len_v0 == 0 or len_v1 == 0 or len_v2 == 0 or len_v3 == 0:
        return [0.0, 0.0, 0.0]
    
    # 计算余弦值并转换为角度,np.clip避免浮点误差导致超出[-1,1]范围
    angle0 = np.arccos(np.clip(np.dot(v0, v1) / (len_v0 * len_v1), -1.0, 1.0)) * 180 / np.pi
    angle1 = np.arccos(np.clip(np.dot(v2, v3) / (len_v2 * len_v3), -1.0, 1.0)) * 180 / np.pi
    angle2 = 180 - angle0 - angle1
    
    return [angle0, angle1, angle2]

path = r"C:\Users\\"
filename = "old_mesh.csv"
datos = np.genfromtxt(path + filename, delimiter=',')

# 删除首行无效的[0,0]
if np.array_equal(datos[0], np.array([0, 0])):
    datos = np.delete(datos, 0, axis=0)

# 清理含NaN的行
if np.isnan(datos).any():
    print("CSV文件包含缺失值或NaN,已自动清理")
    datos = datos[~np.isnan(datos).any(axis=1)]

# 生成Delaunay三角剖分
tri = Delaunay(datos)

# 筛选有效单元:内角都大于0.0001度(可根据精度需求调整阈值)
valid_simplices = []
threshold = 1e-4
for simplex in tri.simplices:
    angles = calculate_triangle_angles(datos, simplex)
    if all(angle > threshold for angle in angles):
        valid_simplices.append(simplex)

valid_simplices = np.array(valid_simplices)
print(f"原始单元数:{len(tri.simplices)},有效单元数:{len(valid_simplices)}")

# 生成VTU文件
output_filename = path + os.path.basename(filename)[0:-4] + '_outputmesh.vtu'
with open(output_filename, "w") as f:
    f.write('<?xml version="1.0"?>\n')
    f.write('<VTKFile type="UnstructuredGrid" version="1" byte_order="LittleEndian" header_type="UInt64">\n')
    f.write('\t<UnstructuredGrid>\n')
    # 更新有效单元数量
    f.write('\t	<Piece NumberOfPoints="{}" NumberOfCells="{}">\n'.format(len(datos), len(valid_simplices)))
    f.write('\t		<Points>\n')
    f.write('\t			<DataArray type="Float64" Name="Points" NumberOfComponents="3" format="ascii">\n')
    for i in range(len(datos)):
        f.write('\t				{} {} {}\n'.format(datos[i][0], datos[i][1], 0))
    f.write('\t			</DataArray>\n')
    f.write('\t		</Points>\n')
    f.write('\t		<Cells>\n')
    f.write('\t			<DataArray type="Int64" Name="connectivity" NumberOfComponents="1" format="ascii">\n')
    for i in range(len(valid_simplices)):
        f.write('\t				{} {} {}\n'.format(valid_simplices[i][0], valid_simplices[i][1], valid_simplices[i][2]))
    f.write('\t			</DataArray>\n')
    f.write('\t			<DataArray type="Int64" Name="offsets" NumberOfComponents="1" format="ascii">\n')
    for i in range(len(valid_simplices)):
        f.write('\t				{}\n'.format((i+1)*3))
    f.write('\t			</DataArray>\n')
    f.write('\t			<DataArray type="UInt8" Name="types" NumberOfComponents="1" format="ascii">\n')
    for i in range(len(valid_simplices)):
        f.write('\t				5\n')
    f.write('\t			</DataArray>\n')
    f.write('\t		</Cells>\n')
    f.write('\t	</Piece>\n')
    f.write('\t</UnstructuredGrid>\n')
    f.write('</VTKFile>')

关键说明

  • 角度阈值:设置threshold = 1e-4是为了规避浮点计算的精度误差,实际可根据网格精度需求调整。如果三点严格共线,函数会直接返回三个0度角,这类单元会被自动剔除。
  • 节点保留:整个过程仅删除无效单元,所有原始节点都会保留在输出的VTU文件中,完全满足需求。
  • 效率优化:如果节点数量极大,可将循环计算改为向量化运算,进一步提升处理速度。

内容的提问来源于Stack Exchange,提问作者Luis Camilo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 22:07:02