news 2026/10/4 6:13:26

Plot3d格式完全指南:从文件结构到Python解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Plot3d格式完全指南:从文件结构到Python解析

你要是翻过 CFD 项目里的原始数据文件,大概率见过后缀叫.xyz、.x、.grid或者.p3d的文件。用文本编辑器一开,满屏数字,第一行往往只有三个整数,后面跟着一长串坐标序列。懂行的人知道这是 Plot3d 网格数据格式,不懂的人看着数字干瞪眼。这篇文章就把 Plot3d 格式一次讲透:文件结构、坐标顺序、二进制细节、解析代码、常见坑点,全都覆盖。不管是学生做课程设计,还是工程师接外部网格,只要拿到这类文件,你都能快速上手处理。适合所有在 CFD、网格生成、后处理工具链里跟数据文件打过交道的人。

1. Plot3d 格式到底是什么,为什么值得专门讲一次

1.1 一个老格式的“简历”

Plot3d 是上世纪七八十年代由 NASA Ames 研究中心推动的一套结构化网格存储约定。当年的 CFD 计算远没有今天这么“图形化”,求解器和后处理程序之间靠的就是纯文本或者二进制数据交换。Plot3d 在这套体系里扮演了一个中间人的角色:它不管几何怎么生成,也不管边界条件怎么设置,只负责把结构化网格的节点坐标按顺序写清楚。

它的核心思路非常朴素:结构化网格的每个节点都可以用(i, j, k)三个整数索引定位,所以只要事先声明三个方向的节点数ni、nj、nk,再按固定顺序把所有节点的x、y、z坐标写出来,就能完整还原一块网格。因为没有单元连接表、没有边界类型标记、没有注释字段,整个文件可以做得极其精简。

正是这种精简让它活到了今天。在开源求解器、网格生成器、后处理脚本之间来回倒腾数据时,Plot3d 往往是成本最低的交换格式。很多经典算例(比如各种翼型、叶栅、外流场算例)的原始网格和流场数据也都是用它存的。说它是 CFD 圈里的“乐高积木”一点不夸张:简单、通用、谁都能拼。

1.2 网格文件与结果文件的分工

Plot3d 格式可以粗略分成两大类:一类存几何,叫网格文件,常见后缀有.xyz、.x、.grid、.g;另一类存流场,叫结果文件或者 Q 文件,常见后缀有.q、.fun、.vortex、.rhoe。

网格文件只放坐标。按块组织,单块文件开头写三个整数ni nj nk,多块文件开头写一个整数nblocks,然后写每个块的维度。接着按块依次写坐标序列。就这么简单。

结果文件则是在网格文件的维度信息基础上,再叠加每个节点上的物理量。比如二维无粘流的守恒变量一般是四个量:密度、x 方向动量、y 方向动量、能量;三维加一个 z 方向动量,变成五个量。文件在维度声明之后会再写一行参数,说明每个点有几个量、有没有附加标志,然后把所有节点的物理量按和坐标一样的顺序排列。

理解这个分工很重要。很多新手把网格文件和结果文件当成两种完全不同的东西,其实它们“骨架”完全一致,只是结果文件在坐标之外多了一组标量/矢量场。后面解析时完全可以共用一套维度读取逻辑。

1.3 适用场景:谁还在用 Plot3d

你可能想问:现在有 CGNS、VTK、HDF5 这些功能更强的格式,为什么还专门研究 Plot3d?我的看法是,它在特定场景下依然很能打。

轻量传输场景里,Plot3d 的体积比其他自描述格式小得多,因为它没有冗余的字典信息。脚本处理场景里,纯文本 Plot3d 用一个几行的 Python 脚本就能解析,不需要引入大型库。数据交换场景里,很多老牌工具(Pointwise、Gridgen、CFL3D、OVERFLOW 等)原生支持 Plot3d,从外部拿到的网格经常就是这个格式。如果你要做网格质量检查、格式转换、算例对比,不会读 Plot3d 会很被动。

当然它也有明显短板:不携带边界条件、不携带坐标系信息、对非结构网格无能为力、注释支持极差。所以它的定位很清楚——结构化网格数据交换的“最小公分母”,而不是万能格式。

2. 文件结构拆解:从一行数字读懂数据布局

2.1 ASCII 单块网格的完整读写流程

先看一个最简单的 ASCII 单块二维网格例子。假设网格在 x 方向有 3 个点,y 方向有 2 个点,z 方向没有变化,可以写成:

3 2 1 0.0 0.5 1.0 0.0 0.5 1.0 0.0 0.0 0.0 1.0 1.0 1.0

第一行的3 2 1分别对应ni、nj、nk。接着是 x 坐标序列,一共3 * 2 * 1 = 6个值:0.0 0.5 1.0对应 j=0 那一行上 i=0,1,2 三个点的 x 坐标,0.0 0.5 1.0对应 j=1 那一行。然后写 y 坐标序列,同样 6 个值,两行分别是 y=0 和 y=1 平面上的坐标。

关键点在于三个嵌套循环的顺序。Plot3d 的坐标写入相当于:

for k in range(nk): for j in range(nj): for i in range(ni): write x[i, j, k] for k in range(nk): for j in range(nj): for i in range(ni): write y[i, j, k] for k in range(nk): for j in range(nj): for i in range(ni): write z[i, j, k]

也就是说,最内层是i,变化最快;然后是j;最外层是k。这和 Fortran 的数组列主序完全一致。你在读文件时,拿到坐标数据后要想还原成三维数组,用 NumPy 的reshape((ni, nj, nk), order='F')就能一步到位。我见过不少人在这里栽跟头,用 C 序order='C'去 reshape,结果画出来的网格扭曲得完全没法看。

三维网格和二维的逻辑一样,只是nk大于 1,坐标序列长度是ni * nj * nk,写完x写y,写完y写z。整个单块网格文件短则几 KB,长则几百 MB,本质上就是“维度声明 + 三大段坐标”这个结构。

2.2 多块文件:块数量与维度声明的两种写法

真实工程里的网格很少是单块的。翼身组合体、叶轮机械通道、带外挂物的飞行器,通常会被切分成多个结构化块,块与块之间共享分界面。多块 Plot3d 文件就是在单块的基础上,把多个块的数据按顺序拼接起来。

多块文件的标准写法是:第一行写一个整数nblocks,接下来给出一共3 * nblocks个维度整数,再往后才是每个块的坐标数据。举例来说:

2 3 2 1 4 3 1 0.0 0.5 1.0 0.0 0.5 1.0 0.0 0.0 0.0 1.0 1.0 1.0 0.0 0.5 1.0 1.5 0.0 0.5 1.0 1.5 ...

这里第一块是3 2 1,第二块是4 3 1。后面按块号依次写每一块的坐标序列,块与块之间没有额外的分隔标记,读的时候必须靠每块的点数去切分数据。

实际使用中你会碰到两种变体。一种是把所有块的维度写在同一行,也叫“一张维度表”风格;另一种是每个块维度单独占一行,可能还混有注释或空行。写解析器时最稳妥的策略不是死磕行结构,而是把整个文本文件先拆成 token 流,去掉空行和注释,再用索引顺序切分。这样无论维度怎么换行都能处理。

还有一点容易引起混乱:一些工具保存“单块”Plot3d 时并不写nblocks=1,而是直接写ni nj nk三个整数。所以判断文件是不是多块,不能只看第一行是不是 1。更可靠的判断方法是统计前几行的整数个数:如果第一个整数后面跟的两个整数共同组成三个一组,那就当单块处理;如果第一行孤立一个整数,后面跟着3 * nblocks个维度,那才是真正的多块描述。许多成熟解析器的自动识别逻辑都是按这个思路做的。

2.3 结果文件(Q 文件)的数据布局

结果文件的作用是把流场数据映射到网格节点上。一个典型的二维 Q 文件长这样:

3 2 1 4 0 1.10 0.20 0.30 2.50 1.20 0.25 0.32 2.60 ...

第一行依旧是维度声明。第二行的含义在不同求解器里有差异,但常见的约定是:第一个整数表示每个节点的物理量个数nq,第二个整数留作附加标志(比如是否包含湍流变量、是否是无量纲量),没有附加信息就写 0。

接下来的数据按节点顺序排列,每个节点写nq个值。以三维可压缩流为例,如果是守恒变量,一般是rho、rho*u、rho*v、rho*w、e;如果是原始变量,可能是p、u、v、w、T。到底按哪种顺序,只能以生成文件的程序手册为准,格式本身不做强制规定。

按我的经验,解析 Q 文件最大的坑不是维度,而是“每个点有几个量”。三维无粘默认 5 个量,三维粘性可能加湍流变量变成 7 个、8 个甚至更多。你在读文件之前最好先去确认这个信息,不要靠猜。否则数据错位之后,画出来的云图会非常奇怪,密度和温度混在一起,颜色分布毫无物理规律。

2.4 二进制版本:Fortran 记录格式与字节序问题

文本文件好懂,但体量大、解析慢,所以大网格常用二进制 Plot3d。二进制不像 ASCII 那样一行一行写数据,而是按 Fortran 的“记录(record)”机制存储。每条记录前后都有一个 4 字节整数,标记这条记录的长度。

读数据时你会看到这样的结构:

  • 一个 4 字节记录头,值通常是 4(表示接下来 4 字节是一个整数);
  • 4 字节的nblocks值;
  • 一个 4 字节记录尾,值也是 4;
  • 一个 4 字节记录头,值等于4 * 3 * nblocks;
  • 所有块的维度整数数组;
  • 对应的 4 字节记录尾;
  • 进入坐标数据记录,每条记录的头尾长度标记可能是12 * ni * nj * nk、16 * ni * nj * nk这种大数字。

坐标数据在一条记录内还是分多条记录,不同工具实现不一样。有的把 x、y、z 合在一条大记录里,有的每个方向单独一条记录。更麻烦的是还有字节序问题:早期 UNIX 工作站多是大端(big-endian),现代 PC 几乎都是小端(little-endian)。读出来全是无规律巨数时,第一反应就应该是字节序不对,把>f4换成<f4或者反过来再试一次。

二进制版的 Q 文件也是同样的记录机制。网格和流场在一个文件里的情况偶尔也能见到,一般前半部分是几何坐标,后半部分紧跟物理量,中间没有明显的分段标记。这种文件解析时尤其要小心,一旦维度算错,后面所有数据全部错位。

3. 实操:用 Python 读写 Plot3d 网格的完整方案

3.1 用纯 Python 实现一个健壮的 ASCII 读取器

先给一个可以直接用的 ASCII 多块网格读取器。它不做花哨的内存优化,但结构清晰,适合自己改造成项目中的工具函数。

import numpy as np def read_plot3d_ascii(filename): tokens = [] with open(filename, "r") as f: for line in f: line = line.split("#")[0] # 去掉注释(如果生成工具加了注释) tokens.extend(line.split()) idx = 0 first = int(tokens[idx]) if first == 1 and len(tokens) >= 4 and tokens[1].replace(".", "").isdigit() is False: # 多块写法:第一行是块数,后面是维度表 nblocks = first idx += 1 dims = [] for _ in range(nblocks): ni = int(tokens[idx]); nj = int(tokens[idx+1]); nk = int(tokens[idx+2]) idx += 3 dims.append((ni, nj, nk)) else: nblocks = 1 idx = 0 ni = int(tokens[idx]); nj = int(tokens[idx+1]); nk = int(tokens[idx+2]) idx += 3 dims = [(ni, nj, nk)] blocks = [] for (ni, nj, nk) in dims: npts = ni * nj * nk x = np.array(tokens[idx:idx+npts], dtype=float) idx += npts y = np.array(tokens[idx:idx+npts], dtype=float) idx += npts has_z = (nk > 1) if has_z: z = np.array(tokens[idx:idx+npts], dtype=float) idx += npts else: z = np.zeros_like(x) # 还原成三维数组,i 方向在内存中变化最快 x = x.reshape((ni, nj, nk), order="F") y = y.reshape((ni, nj, nk), order="F") z = z.reshape((ni, nj, nk), order="F") blocks.append((x, y, z)) return blocks

这个函数有几个地方值得说明。第一,我用line.split("#")[0]先去掉注释。很多软件虽然不支持 Plot3d 注释,但用户手动改文件时会加#标记,这一步能避免踩坑。第二,判断单块还是多块时,只根据 token 流内容判断:如果第一行只有一个整数,而且它不是1个块时,正好配合后面的维度表;如果第一行就是三个整数,则直接按单块解析。第三,坐标数据转换成三维数组时用了order="F",这是为了让数组索引顺序和写入顺序一致。这样后续访问x[i, j, k]时,内存顺序就对应 Plot3d 的数据流,不容易混淆。

3.2 二进制读取器的实现思路与记录校验

二进制读起来稍微复杂一点。核心是处理 Fortran 记录。我一般这样实现一个读取记录的辅助函数:

import struct import numpy as np def read_record(f, byteorder, dtype): head = f.read(4) if len(head) < 4: return None (n,) = struct.unpack(byteorder + "i", head) data = np.frombuffer(f.read(n), dtype=np.dtype(byteorder + dtype)) tail = f.read(4) (m,) = struct.unpack(byteorder + "i", tail) if m != n: raise ValueError("Fortran record length mismatch") return data

调用时先尝试一种字节序。比如默认假设小端,数据类型是单精度浮点f4:

with open(filepath, "rb") as f: nblocks = int(read_record(f, "<", "i4")[0]) dims = read_record(f, "<", "i4").reshape((-1, 3)) blocks = [] for ni, nj, nk in dims: npts = int(ni * nj * nk) coords = read_record(f, "<", "f4") x = coords[0:npts].reshape((ni, nj, nk), order="F") y = coords[npts:2*npts].reshape((ni, nj, nk), order="F") z = coords[2*npts:3*npts].reshape((ni, nj, nk), order="F") blocks.append((x, y, z))

这里有个工程细节:有些二进制文件里,一个块的 x、y、z 不是合在一条记录里,而是分成三条记录。如果合并读取失败,也就是read_record返回的记录长度不等于3 * npts * 4,你就可以改成分别读三条再拼接。我的做法是先检测一次记录头,如果长度对不上,就按“每方向一条记录”重新读。

还有一点,很多老式文件的坐标不是单精度,而是双精度f8。判断方法很简单:读一条记录看长度,如果长度按f4解释时得到的点数和维度算出的ni*nj*nk差一个量级,再按f8试试。这属于经验活,但写解析器时把这两个分支都处理掉,基本就够用了。

3.3 写出网格:ASCII 与二进制如何选

写网格比读网格容易,因为布局完全由你控制。如果是给其他工程师看的调试用网格,我推荐写 ASCII,方便别人直接用文本编辑器检查。输出的格式可以这样组织:

2 3 2 1 4 3 1 0.0 0.5 1.0 0.0 0.5 1.0 0.0 0.0 0.0 1.0 1.0 1.0 ...

用 Python 写的时候可以这样:

with open("out.xyz", "w") as f: f.write(f"{nblocks}\n") for ni, nj, nk in dims: f.write(f"{ni} {nj} {nk}\n") for block in blocks: x, y, z = block xyz = np.stack([x, y, z], axis=0) for comp in range(3): data = xyz[comp].flatten(order="F") f.write(" ".join(f"{v:.8f}" for v in data) + "\n")

对很大的网格,ASCII 写的文件会非常占空间。一个千万级节点的网格,光坐标文件就轻松超过 500 MB,写起来慢,后续读取也慢。这种时候优先写二进制,好处是文件小、读写快、精度可控。二进制的写法和读取对称:先写 nblocks 记录,再写维度记录,然后每个块写坐标记录。

写二进制时最容易犯的错误是不转换字节序。如果你用 numpy 直接写float32数组,默认是机器本机字节序,在普通 PC 上是小端;如果对方工具是按大端读取的,就会得到一堆乱码。稳妥做法是先调用numpy.ndarray.astype把 dtype 改成带明确字节序的格式,比如>f4或<f4,再写入文件。这个习惯能省掉大量排查时间。

3.4 坐标顺序核对:i、j、k 方向必须一致

有人可能会问:我用reshape((ni, nj, nk), order="F")读出来的数组,索引[0, 0, 0]是文件的哪个点?答案是文件里第一个坐标点。这个点到底位于几何的哪个角,则取决于生成网格时的节点编号规则。

很多 CF D 代码要求i方向沿某个物理边界(比如翼型弦向),j方向沿展向,k方向沿法向。当你从外部拿到网格时,第一件事就是确定代码或者网格生成工具约定的方向和你后续处理时是否一致。一个常见做法是先把 x、y、z 的最大值和最小值所在的索引打出来,和预期几何特征对照。

for bi, (x, y, z) in enumerate(blocks): xmin_idx = np.unravel_index(np.argmin(x), x.shape) xmax_idx = np.unravel_index(np.argmax(x), x.shape) print(block index, xmin_idx, xmax_idx)

如果最小 x 点出现在(0, 0, 0),而你的几何预期那个角落应该是最小 x,说明顺序没问题。如果完全对不上,比如最小 x 跑到了(ni-1, 0, 0),那要么是文件的维度声明颠倒,要么坐标序列写反了。这种索引层面的检查比肉眼看云图快得多,建议每次处理新文件都跑一遍。

还有一个很容易被忽视的点:有些工具保存 2D 网格时nk要么不写,要么写 1,但坐标流里没有 z 坐标。你在读取时如果按“有 z 坐标”的分支去取数,会把后面的数据吞掉。所以我写代码时总是先看nk是否大于 1,再决定要不要读取第三段坐标,而不是简单根据维度个数判断。

4. 常见问题与排查技巧实录

4.1 进入排查前的第一步:确认文件到底是什么

拿到一个后缀不明的文件,先别急着写解析脚本。我习惯先做三件事。第一,用十六进制编辑器或xxd打开文件头部,看是纯文本还是二进制。如果文件头有\x00这种不可打印字符,基本可以确定是二进制。第二,如果是文本,用文本编辑器或者head -20看前五行数字,判断是否具备 Plot3d 特征:有维度声明、后面跟着坐标序列。第三,看看文件大小是否能整除可能的记录长度。

这一步能快速排除一类文件:那些其实是 CGNS、VTK 或者 NaN 网格的文件,只是后缀起成了.xyz。我见过有人拿着一份 VTK 结构化网格文件硬套 Plot3d 解析逻辑,结果当然是一团糟。遇到这种情况,先搞清楚对方给你的到底是什么格式,再谈解析。

4.2 二进制读出来全是乱码/负百万级数据

这是我最常被问到一个问题:用numpy.fromfile直接读二进制 Plot3d,数据全是-1.7e+38这种值。原因基本就两个。

第一个是没处理 Fortran 记录头尾。直接fromfile会把每条记录前面的长度整数当成浮点数据,导致整个序列错位。解决办法是先解析记录长度,或者跳过每一条记录的 4 字节头和 4 字节尾。

第二个是字节序反了。如果你发现读出的数据数量是对的,但值完全没有物理意义,而且里面经常出现超大正数或负数,那就把字节序反过来试一次。在 numpy 里最简单的方式是读取时直接指定 dtype,比如np.dtype('>f4'),如果换成了<f4数据就正常,说明文件就是另一个字节序。

排查时可以先用一个最小例子:构造一个 2x2x1 的网格文件,分别用 ASCII 和二进制保存,然后用自写解析器去读。如果这个都过不了,问题通常出在记录结构,而不是后面的维度处理。

4.3 多块拼接与重叠边界检查

多块 Plot3d 文件解析成功后,下一步要关注的是块与块之间的关系。由于文件里没有连接信息,块之间的共享分界面只能靠几何位置来匹配。简单检查方法是把每块的最小和最大坐标范围打印出来,两两取交集,如果某个方向的区间有重合,那很可能就是共享边界。

更进一步的检查是比对重合面上的节点坐标。先按容差把两块中距离小于某个阈值的点找出来,看看数量是否符合预期。如果两块网格在同一个物理面上,一方有 51 个点、另一方有 51 个点,那么匹配点数量应该是 2601 或接近这个数。如果数量差很多,说明两块网格的拓扑不对齐,拼接处会有缝隙,后续计算可能出问题。

这里我建议写一个简单的表面点最近邻匹配脚本,用scipy.spatial.cKDTree或者粗暴的双循环判断小网格都能做。Plot3d 本身不管连接关系,所以这些检查必须由使用者自己做。

4.4 可视化看网格是否有畸变

排查几何问题最直观的手段就是可视化。对于二维某个截面,把网格线画出来看是否有交叉、拉断、负体积。最简单的脚本是这样:

import matplotlib.pyplot as plt x, y, z = blocks[0] k = 0 fig, ax = plt.subplots() for j in range(y.shape[1]): ax.plot(x[:, j, k], y[:, j, k], "b-", lw=0.4) for i in range(x.shape[0]): ax.plot(x[i, :, k], y[i, :, k], "r-", lw=0.4) ax.set_aspect("equal") plt.show()

画出来之后,看着网格线交叉、折叠,基本就是维度顺序颠倒或者坐标流解析错误。如果只是局部网格比较密或比较疏,那是正常现象;但如果某几条线突然穿过别的线,那就有问题了。

三维网格可以用pyvista直接显示表面网格。把(ni, nj, nk)的三维数组塞给StructuredGrid,再save成 VTK 文件,或者直接plot()。这样能快速定位严重的负体积和畸变区域。

4.5 常见问题速查表

为了方便参考,这里整理一张速查表,覆盖我实际工作中遇到的典型问题。

症状可能原因处理方式
第一行只有一个整数,后续解析错位多块文件,维度表没按多块处理判断 nblocks,按维度表切分
坐标序列长度对不上维度声明写错,或读取时把 z 坐标漏读了核对 ni、nj、nk 与坐标段长度
读出的坐标全是超大数字节序不对切换<f4和>f4再试
解析失败,找不到记录尾长度文件不是 Fortran 记录,或按每方向一记录读取时错位用十六进制编辑器查看文件头,确认记录结构
某一块坐标严重偏移多块时块序和预期不一致打印每块坐标范围,按几何位置重新排序
网格线交叉维度声明与坐标流顺序颠倒用reshape(order="F")并核对方向
结果文件的物理量范围离谱nq 参数搞错,或物理量顺序与预期不同查生成程序文档,确认 nq 和变量顺序

5. 从 Plot3d 到其他格式:工具链衔接建议

5.1 用 Matplotlib 做网格质量初检

前面已经写了画网格线的代码,这里再补充一个判断负体积的思路。对结构化六面体网格,你可以对每个单元计算 Jacobian 行列式。如果某个单元的 Jacobian 小于等于零,说明单元翻转或者退化,是网格质量事故。

理论公式不复杂:对每个六面体单元,取 8 个顶点,把单元切分成若干个四面体,分别计算体积,只要所有四面体体积为正,单元基本就是好的。更简单的做法是利用单元三个方向边向量,计算混合积。如果所有单元都正常,混合积符号一致。

# 简化版:对三维结构化网格计算每一层的单元面积方向 # 这只是初筛,更严格的需要逐单元计算 Jacobian x, y, z = blocks[0] ni, nj, nk = x.shape detJ = np.zeros((ni-1, nj-1, nk-1)) for i in range(ni-1): for j in range(nj-1): for k in range(nk-1): # 取沿 i、j、k 三个方向的边向量 ei = np.array([x[i+1,j,k]-x[i,j,k], y[i+1,j,k]-y[i,j,k], z[i+1,j,k]-z[i,j,k]]) ej = np.array([x[i,j+1,k]-x[i,j,k], y[i,j+1,k]-y[i,j,k], z[i,j+1,k]-z[i,j,k]]) ek = np.array([x[i,j,k+1]-x[i,j,k], y[i,j,k+1]-y[i,j,k], z[i,j,k+1]-z[i,j,k]]) detJ[i,j,k] = np.dot(ei, np.cross(ej, ek))

如果detJ出现负值,把对应编号打印出来,定位到坐标数组中,再去原始文件里检查那几行数据。这个方法虽然简单,但用来拦截“坐标解析错位”这种系统性错误非常有效。

5.2 转成 VTK/CGNS 的快速通道

Plot3d 网格在结构化格式里属于比较“原始”的存在,很多后处理和可视化工具更习惯用 VTK 或者 CGNS。转换的思路很简单:把坐标数组按块填充到目标数据结构里。

用pyvista写 VTK 只需要几行:

import pyvista as pv for x, y, z in blocks: grid = pv.StructuredGrid(x, y, z) grid.save("block.vtk")

这里传进去的x、y、z必须是三维数组,维度顺序是(ni, nj, nk),正好和我们前面用order="F"还原出来的结果一致。如果目标工具按 C 序索引,也只需要转置轴即可,比如.transpose(2, 1, 0),本质是把 k 变成最内层。

如果是做 CFD 前处理,CGNS 是更合适的方向,因为它能存储边界条件、网格连接等额外信息。转换时需要注意 CGNS 的 Zone 也是按结构化块组织的,每个 Zone 需要声明维度和坐标。用cgnspy或者pycgns这类库填入数据时,每条记录对应 Plot3d 的一个块,顺序不变即可。

我个人的建议是,在没有特别需求时,先用 VTK 做可视化检查,确认几何没问题后再转入下一步。直接拿解析后的数据去做数值计算,如果前期坐标就有问题,后面所有结果都不可信。转格式本身不是难点,难的是保证坐标顺序和块序在转换过程中不被搞乱。

最后再分享一个我工作上的习惯:凡是新接触一套网格数据,我一定先打印出每个块的最小和最大坐标,与文档里的几何范围对照,再做一次可视化初检,最后才真正进入网格转换或者流场读取。这个流程帮我校正了不少低级错误,也节省过很多和同事来回对文件的沟通成本。Plot3d 格式虽然朴素,它的规律性很强,把数据布局吃透之后,你会发现不管换什么工具链,处理起来都相当顺手。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 6:12:37

不装ROS用Python解析bag文件:提取图像与IMU数据全攻略

每次拿到.bag文件&#xff0c;我的第一反应都是“又要装ROS才能看了吗”。尤其是有时候别人发来一个VINS Fusion的bag文件&#xff0c;我只是想提取里面的图像和IMU数据做标定&#xff0c;为这点事去装一个完整的ROS环境实在不划算。如果你也遇到类似的情况——主力机器是Windo…

作者头像 李华
网站建设 2026/10/4 6:12:15

STM32+MRAM工业数据存储方案:MR25H40CDF驱动与掉电保护实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 6:11:44

基于SpringBoot+Vue咖啡售卖商城平台的设计与实现

选题背景与意义 随着互联网技术的迅猛发展和消费者生活方式的深刻变革&#xff0c;线上购物已成为人们日常消费的重要方式。尤其在快节奏的城市生活中&#xff0c;咖啡作为一种兼具提神醒脑与社交属性的饮品&#xff0c;逐渐从传统的咖啡馆消费模式转向多元化、便捷化的购买渠道…

作者头像 李华
网站建设 2026/10/4 6:09:25

SS7七号信令协议分析实战:从分层原理到抓包排障

简介&#xff1a;聚焦七号信令&#xff08;SS7&#xff09;协议的入门与进阶学习资料&#xff0c;面向通信工程学生、电信运维人员及协议研究者&#xff0c;以图文方式解析SS7协议栈的层次结构、信令点/转接点组网方式及MTP、SCCP、TCAP等核心模块的工作原理。压缩包共607个文件…

作者头像 李华
网站建设 2026/10/4 6:07:19

基于STM32F417ZG与MR25H40CDF的SPI MRAM非易失存储方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 6:06:50

YOLO+VOC双标签生活用品数据集:4500张图开箱即用训练指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华