资讯详情

资讯详情

Gmsh网格转Exodus II格式:Python转换器实现与Peridigm应用

简介这是一款面向计算力学与多物理场仿真用户的网格格式转换工具基于 Python 实现可将 Gmsh 生成的 .msh 网格文件转换为 Exodus II 格式以便在 Peridigm 等仿真软件中直接使用同时兼容 ParaView 生态。支持的网格类型涵盖三节点三角形与四节点四边形等常见二维单元覆盖典型 Gmsh 建模范式。资源包内含 25 个文件约 861KB核心包括 convert.py 转换脚本、mesh.py 网格处理模块、3 个示例 .msh 网格文件以及 6 张效果示意图另有安装说明、依赖配置与许可证文件结构清晰便于快速上手。对需要将网格模型从 Gmsh 流转至 Exodus II 工作流的仿真工程师和研究人员而言这份工具包可直接按文档配置 VTK 与 Python 环境后运行省去自行编写转换器的麻烦。已有 563 人学习下载值得从事近场动力学或有限元仿真建模的读者参考使用。1. 这个转换器解决什么一场“格式不兼容”的折腾先讲个场景。有位同事在 Gmsh 里花了整整一下午画好了一个带裂纹的二维平板网格单元质量检查全绿物理分组也设置得清清楚楚结果拿到 Peridigm 那边一跑直接报错说网格文件读不进去。Peridigm 认的是 Exodus II 格式也就是 .e 或 .exo 文件跟 Gmsh 默认输出的 .msh 完全是两套体系。这个 gmsh-exodus-converter 就是干这个的用 Python 把 Gmsh 生成的网格转成 Exodus II 格式让 Peridigm 能读取顺带让 Paraview 打开结果时不至于抓瞎。适合三类人一是做近场动力学仿真、想在 Peridigm 里用 Gmsh 画网格的二是手里有大量 .msh 文件需要在不同求解器之间迁移的三是想在 Python 里批量处理网格格式、不依赖图形界面手动导出的那批人。这篇文章会把这套转换的链路、核心代码和踩坑细节都过一遍末尾给出可以直接上手改的扩展思路。2. 架构拆解从 .msh 到 .e 的完整映射链路2.1 两种格式的本质差异别把转换想成“改后缀”很多人第一次接触时都以为格式转换就是换个扩展名实际完全不是。Gmsh 的 .msh 格式是文本文件按 $Nodes、$Elements 这样的分节存储节点有编号、有坐标单元有类型编号、有拓扑连接。Exodus II 是基于 NetCDF 的二进制格式它把网格拆成若干“块”Block每个块对应一组相同类型的单元节点按维度分量存储还有专门的节点集Node Set和边集Side Set用于施加边界条件。换句话说转换的核心不是文件读写而是把 Gmsh 的“物理分组”翻译成 Exodus II 的“块和集合”。这一步如果没做好Peridigm 打开后要么缺单元要么边界条件跑偏。从库的依赖也能看出这条链路的重点。读取 .msh 用的是 meshio这个库对 Gmsh 的支持比较成熟能直接拿到节点坐标、单元连接和物理分组信息。写出 .e 文件用的是 h5py直接操作 NetCDF 的底层存储。中间的维度转换、ID 映射、字段拼接靠 numpy 完成。完整架构可以理解为三段式读取解析、数据重组、二进制写出。2.2 主循环与输出文件的 NetCDF 结构转换脚本的主流程非常短核心就三步。第一步用 meshio 读入 .msh拿到 mesh.points 和 mesh.cells第二步遍历所有单元类型把 Gmsh 的单元编号映射成 Exodus II 的块 ID同时把节点坐标整理成两个数组第三步把节点 ID、坐标数组、单元连接数组写进 .e 文件。有意思的是Exodus II 格式中所有 ID 都是从 1 开始编号的而 meshio 返回的 Gmsh 节点编号也是从 1 开始这算是个好消息省了一次减一操作。具体到输出文件内部核心变量是 coord 和 connect 两个数组。coord 是一个 (节点数, 3) 的浮点数组存的是每个节点的 x、y、z 坐标connect 是一个 (单元数, 节点数) 的整数数组存的是每个单元由哪些节点构成比如三角形单元就是三列四面体单元就是四列。这个数组在 Exodus II 里是按块存储的块 ID 在 eb_prop1 这个属性里标记Peridigm 读取时靠这个 ID 区分不同的单元族。所以转换时不管 Gmsh 物理分组有多少层最终落到文件里的块数量是由单元类型数量决定的而不是由物理分组数量决定。2.3 参数映射表Gmsh 单元编号到 Exodus II 的对应关系Gmsh 单元类型码含义Exodus II 块类型节点数1两节点线单元BAR222三节点三角形TRI333四节点四边形QUAD444四节点四面体TETRA445八节点六面体HEX8815单节点点SPHERE1这张表的对应关系是整个转换器最核心的资产。Peridigm 支持的最小单元就是单节点点单元所以很多近场动力学模型在 Gmsh 里用点单元来表示粒子转换后落到 Exodus II 里就是 SPHERE 块。做二次开发时如果要支持新的单元类型比如六节点三角形或二十节点六面体只需要在映射表里加一行再处理节点排列顺序即可。映射逻辑本身是字典驱动的改起来比硬编码要安全得多。提示Exodus II 的单元类型字符串是写死的拼错一个字母 Peridigm 直接拒绝打开。建议转换后先用 Paraview 打开看一眼再做仿真计算。3. 核心实现单元分组与节点集转换的关键逻辑3.1 从 meshio 对象到块字典的数据重组转换逻辑的入口是读取 meshio 对象后的一次遍历。下面这段代码是整个转换器最核心的部分它把原始网格数据拆成“按块分组”的结构import meshio import numpy as np from collections import defaultdict def read_msh_and_group(msh_path): mesh meshio.read(msh_path) # block_map 把 meshio 的单元类型名映射为 Exodus II 块类型 block_map { line: BAR2, triangle: TRI3, quad: QUAD4, tetra: TETRA4, hexahedron: HEX8, vertex: SPHERE, } blocks defaultdict(list) for cell_block in mesh.cells: cell_type cell_block.type if cell_type not in block_map: print(f[警告] 忽略暂不支持的单元类型: {cell_type}) continue exo_type block_map[cell_type] for conn in cell_block.data: # 存储时统一转为 0-based后续写出时再加 1 blocks[exo_type].append(conn.astype(np.int64) 1) points mesh.points.astype(np.float64) return points, blocks这段代码的逻辑是把 Gmsh 的物理分组彻底打散重新按单元类型归拢。有些 .msh 文件里一个物理分组内混杂了三角形和四边形单元这种情况如果直接按分组转换Exodus II 里就得开两个块。这里有意的设计是先按类型拆再按类型聚合确保每个块只包含一种单元类型避免 Peridigm 读取时出现“块内单元维度不一致”的报错。3.2 边界条件与节点集Peridigm 能不能算对的关键前面处理了单元但近场动力学仿真里边界条件往往不是加载在单元上而是加载在节点上。Peridigm 读取 Exodus II 时通过节点集 ID 来识别边界节点然后在输入文件里对每个节点集施加位移约束或力载荷。所以转换器必须把 Gmsh 里的物理点分组转换成 Exodus II 的节点集。核心逻辑如下def extract_node_sets(mesh, point_physical_names): node_sets {} point_data mesh.point_data physical_tags mesh.cell_data.get(gmsh:physical, []) # Gmsh 用整数标签标记节点所属物理分组 if gmsh:physical not in mesh.cell_data: return node_sets # 先收集每个物理标签对应的所有节点 tag_to_nodes defaultdict(set) for block_idx, cell_block in enumerate(mesh.cells): tags physical_tags[block_idx] for local_idx, tag in enumerate(tags): conn cell_block.data[local_idx] tag_to_nodes[tag].update(conn.tolist()) for tag, node_list in tag_to_nodes.items(): if tag in point_physical_names: node_sets[str(tag)] sorted(node_list) return node_sets这里有个容易踩的坑Gmsh 里物理分组既可以作用在单元上也可以作用在点上。作用在点上的时候physical tag 标记的是单节点单元的标签作用在单元上时标记的是整个单元的标签。上面的代码同时处理了两种情况先遍历单元数据找到所有节点再根据标签筛选出需要转成节点集的点。如果只是简单地把每个物理标签里的节点拎出来很容易把单元内部的节点也当成边界节点导致后续载荷施加错位。3.3 写出 Exodus II 文件的 h5py 实现数据重组完成后最后一步是写出 NetCDF 格式的 .e 文件。这里用的是 h5py 直接创建 HDF5 文件然后写入 Exodus II 规定的变量名和属性。Exodus II 格式对 NetCDF 变量名有严格要求比如 coord 必须是三维数组、connect 必须是二维数组、eb_prop1 必须存在且包含块 ID。下面这段代码展示了写出过程import h5py def write_exodus(exo_path, points, blocks, node_sets): num_nodes points.shape[0] num_elems sum(len(v) for v in blocks.values()) num_blocks len(blocks) with h5py.File(exo_path, w) as f: # 必需属性维度声明 f.attrs[num_nodes] num_nodes f.attrs[num_elem] num_elems f.attrs[num_dim] points.shape[1] # 节点坐标数组Exodus II 要求按 (num_dim, num_nodes) 存储 coord np.zeros((points.shape[1], num_nodes)) for dim in range(points.shape[1]): coord[dim, :] points[:, dim] f.create_dataset(coord, datacoord) # 每个块的连接数组和块 ID connect_group f.create_group(connect) eb_prop1 np.zeros(num_blocks, dtypenp.int64) block_id 1 for exo_type, conn_list in blocks.items(): conn_array np.array(conn_list) connect_group.create_dataset(fconnect{block_id}, dataconn_array) # 写入块类型字符串Peridigm 依赖这个识别单元构型 f.create_dataset(felem_type{block_id}, datanp.bytes_(exo_type)) eb_prop1[block_id - 1] block_id block_id 1 f.create_dataset(eb_prop1, dataeb_prop1) # 节点集 ns_group f.create_group(ns) for ns_id, node_list in node_sets.items(): ns_group.create_dataset(fnode_set{ns_id}, datanp.array(node_list))这段代码里有两个细节值得注意。第一coord 数组的维度顺序是 (维度数, 节点数)而不是直觉上的 (节点数, 维度数)这是 Exodus II 的老传统写反了 Paraview 会显示奇怪的拉长图形。第二elem_type 变量必须以字节字符串的形式写入直接写 Python str 会被 h5py 当成 UTF-8 字符串存成可变长类型老版本 Peridigm 读不出来。凡是遇到二进制格式解析报错的多半就是这两处细节没对齐。4. 跑通指南命令行参数与验证流程4.1 环境准备与依赖安装这个转换器是纯 Python 实现的依赖项集中在 meshio、numpy、h5py 三个库上。建议用虚拟环境安装避免把系统 Python 搞乱。新建一个虚拟环境后执行安装命令装完就能 import 测试python -m venv .venv source .venv/bin/activate pip install meshio numpy h5py python -c import meshio, h5py; print(依赖安装成功)如果电脑上已经装过 Anaconda也可以直接在 conda 环境里跑省去 venv 步骤。依赖本身没有版本冲突风险meshio 和 h5py 的更新速度都不快我用的时候是最新版本没碰到兼容性问题。4.2 命令行入口与参数说明转换器本身是 Python 脚本调用方式非常直接。假设你已经有一个 Gmsh 导出的文件 plate.msh执行下面的命令就能得到 plate.epython gmsh_to_exodus.py plate.msh -o plate.e脚本支持的参数不多但每个都覆盖了一种真实使用场景。下面是用 --help 查看的参数列表参数作用默认值input.msh输入网格文件路径必填无-o, --output输出文件路径输入文件名替换扩展名为 .e--dim强制指定模型维度 2 或 3自动探测--node-set-tags逗号分隔的物理标签列表转成节点集空--dim 参数的存在是因为有些二维网格在 Gmsh 里存成了三维坐标z 全为 0Peridigm 打开后会当成三维模型计算结果完全不对。这种情况下用 --dim 2 强制降维转换器会丢掉 z 坐标列。--node-set-tags 则是把 Gmsh 物理标签里指定的点分组提出来转成节点集给 Peridigm 施加边界条件用。4.3 完整的验证流程从转换到 Peridigm 跑通转换完成后不能直接埋头去算先花两分钟做一次完整性检查。打开 Paraview加载生成的 .e 文件快速看三件事模型形状是否和 Gmsh 里一致、单元数量是否吻合、边界节点集是否显示出来。Paraview 加载 Exodus II 文件时会在 Pipeline Browser 里显示所有的边块和节点集展开就能看到每个块的单元数量。这一步能筛掉 80% 的转换错误。# 转换后快速验证用 Python 读取并打印块信息 python -c import h5py f h5py.File(plate.e, r) print(节点数:, f.attrs[num_nodes]) print(单元数:, f.attrs[num_elem]) print(块数:, len(f[connect].keys())) for key in f[connect].keys(): print(f块 {key}: {f[\connect\][key].shape[0]} 个单元) 如果打印结果和 Gmsh 里的统计一致就可以放心提交给 Peridigm 了。Peridigm 的输入文件里需要指定网格文件路径并在 MATERIAL 和 BOUNDARY 部分引用对应的块 ID 和节点集 ID。注意 Peridigm 里的块 ID 是从转换后的文件里读出来的不是 Gmsh 里的物理标签。这一点搞混了会出现“材料没赋上”的诡异错误——Peridigm 默认给未指定材料的块分配默认参数计算结果看起来没垮但完全不是你要的模型。5. 避坑记录五个格式转换的常见问题5.1 单元 ID 从 0 开始还是从 1 开始现象转换成功Paraview 打开也能显示模型但提交 Peridigm 计算时报“单元 ID 越界”或“节点 ID 不存在”。原因Exodus II 要求节点和单元 ID 从 1 开始而某些旧版本 meshio 返回的节点编号是 0-based。如果代码里直接沿用 meshio 的索引连接数组里就会出现 0和 Exodus II 的 1-based 冲突。解决在数据重组阶段统一对连接数组加 1注意不要对坐标数组加坐标没有 ID 概念。上面 3.1 节的代码里特意写了conn.astype(np.int64) 1就是干这个的但如果你是自己在二次开发里写的转换逻辑很容易漏掉这一步。5.2 二维模型被当成三维现象Gmsh 里画的是二维平面网格转换后用 Paraview 打开发现模型在 z 方向被拉伸或倾斜。原因Gmsh 的 .msh 文件里所有节点都带三个坐标分量二维模型的 z 坐标全部为 0。Exodus II 文件里如果 num_dim 属性是 3Paraview 就会用三个维度渲染看起来正常但如果 Peridigm 里用了平面应力模型它期望的是二维格式此时必须把 num_dim 设为 2。解决转换器里加一个 --dim 参数强制重写 num_dim 属性和 coord 数组的维度。我在实际使用中几乎每次转二维模型都会手动指定不要依赖自动探测。5.3 块内混入不同维度的单元现象Peridigm 读取时报“块 3 包含维度不一致的单元”或者 Paraview 里某些单元显示为红色。原因Gmsh 物理分组里如果同时选了三角形和四边形单元转换时没有按类型分组全部塞进了同一个块。Exodus II 里一个块只能包含同一种单元类型混着写进同一块肯定炸。解决转换前在 Gmsh 里就按单元类型分成不同的物理分组或者在转换器里做类型检查遇到混排时自动拆分成两个块。第二种做法更稳因为很多历史网格文件没办法轻易改。5.4 物理标签和节点集对应错位现象Peridigm 里施加了位移约束但计算结果完全不对像是约束被加到了模型内部的节点上。原因Gmsh 里创建物理分组时如果你选中的是“点”对应的物理标签标记的是单节点单元而代码里遍历单元时拿到的是含该点的所有高维单元把这些高维单元的节点全部并进了节点集。解决用 meshio 读取时检查mesh.cells里是否有类型为 vertex 的块只有 vertex 块里的节点才能当节点集。这个 bug 是转换器最容易踩的坑效果隐蔽Peridigm 不报错但结果必错。5.5 生成的 .e 文件小得可疑现象一个 .msh 文件转成 .e 后只有几 KB打开看只有一个空壳。原因读取 .msh 时物理分组标签为空或者 meshio 返回的 cell 数组为空。这通常是因为 Gmsh 导出时只选了“物理分组内的单元”而不是“所有单元”导致文件里根本没有单元数据。解决转换前在 Gmsh 的 Mesh 菜单里勾选“Export all elements”或者在转换脚本里加一个判断如果解析出的单元总数小于 Gmsh 界面显示的单元数直接报错中断。6. 二次开发扩展单元类型与字段映射的改造思路最后一个环节聊怎么改。如果你不只是想转一个文件而是想把转换器嵌到自己的仿真流程里需要考虑三个扩展方向。第一个是支持更多单元类型前面那张映射表只有六行实际工程里还可能遇到五节点金字塔单元、二十节点六面体单元。扩展方法很简单在 block_map 里新增一行映射在 Exodus II 的 elem_type 变量里填对应的字符串即可。注意金字塔单元在 Gmsh 里的节点顺序和 Exodus II 里的节点顺序不同如果直接转会导致单元扭曲处理办法是在连接数组写入前按目标顺序重排索引列。第二个扩展方向是字段映射。Peridigm 的仿真结果往往需要把初始损伤、温度或其他场变量从 Gmsh 转到 Exodus II 文件里。Exodus II 支持 nodal variables 和 element variables 两组数据集命名规矩是vals_nod_var1、vals_elem_var1这种递增编号。做法是在 h5py 写入阶段创建新的数据集值从 mesh.point_data 里对应取数。我在给一个热力耦合模型做转换时就是这么干的把 Gmsh 里算好的温度分布直接带进了 Peridigm。第三个扩展方向是批量转换。如果你想处理一个文件夹下的几千个文件可以在命令行入口加一个 glob 参数循环调用转换函数顺便把每个文件的块信息汇总输出成 JSON 日志。我一般会把日志里的单元数、块数、节点集数保存下来后面做网格质量审查或模型简化时直接拿这些数据筛一遍省得每个文件都打开 Paraview 看一遍。从那次批量转换后我每次改完转换器都会强制跑一遍“读入-转换-回读打印块信息”的三步验证确认连一个文件都不出错了才敢往外发。这算是我在格式转换这条路上用血泪换来的习惯希望帮到你。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →