简介:三维有限元分析中,四面体单元上的数值积分是求解刚度矩阵与载荷向量的关键步骤。面向数值计算研究者和工程仿真开发者,这份资源包聚焦于四面体上的高斯积分方法,提供基于Matlab的实现,涵盖积分点生成、权重计算以及形函数在四面体上的积分处理,可直接用于高阶单元的自定义开发。包内共3个文件,核心为Matlab脚本,另有ZIP归档与TXT说明文档,总大小约4KB,结构简洁;脚本代码便于逐行调试,说明文本则帮助梳理算法原理。目前已有245人学习下载,适合正在研习数值积分算法或需要快速在工程项目中应用四面体积分的中高级开发者。借助示例程序,读者能直观比较不同阶数积分点的分布差异,理解从低阶到高阶的递推过程,并在实际三维模拟中复用现成的积分模块,降低重复推导和编码成本,有效提升前处理效率。
1. 四面体上的高斯求积:为什么不能用六面体那套张量积打法
大多数三维有限元网格最终都被剖成四面体,可四面体上的数值积分却比六面体麻烦一个量级。六面体上可以按三个方向分别取高斯点做张量积,四面体的参考区域本身带约束(x+y+z≤1),三个坐标并不独立,强行张量积只会得到落在区域外或严重冗余的积分点,精度账怎么算都对不上。Gauss Quadrature for Tetrahedra 要解决的正是这个问题:在四面体上合理布置积分点、配权重,用最少的点算准单元刚度矩阵里的积分。本文从构造原理讲到物理单元映射,再到选阶数和踩坑,完整走一遍这个方案的落地路径。
2. 从参考四面体讲起:坐标系约定与积分规则的设计原理
2.1 体积坐标:四面体积分的天然坐标系
处理四面体积分时,我很少直接用笛卡尔坐标,而是把积分表都做成体积坐标的形式。参考四面体取四个顶点 P1=(0,0,0)、P2=(1,0,0)、P3=(0,1,0)、P4=(0,0,1),内部任意一点可以写成:
x = λ1·P1 + λ2·P2 + λ3·P3 + λ4·P4
其中 λ1、λ2、λ3、λ4 就是体积坐标。因为参考四面体的四个顶点坐标正好是标准基向量,λ1=x、λ2=y、λ3=z、λ4=1-x-y-z,四个数之和恒等于 1。
用体积坐标发布积分点表有三个好处。一是数值稳定,所有坐标分量都落在 0 到 1 之间,不像某些归一化坐标系会冒出负数;二是对称性一目了然,积分点相对顶点、棱、面的关系从坐标分量就能读出来;三是从参考单元映射到任意物理四面体时,直接拿体积坐标做线性组合即可,不需要额外的坐标变换。这套约定也是绝大多数论文和开源库默认的格式,照着用能少踩很多坑。
2.2 对称群与积分点轨道的设计套路
四面体上构造高斯型积分规则,核心工具是对称群。四面体的对称群有 24 个对称操作,包括绕顶点与对面中心的旋转、绕对棱中点的 180° 旋转等。如果积分点布置成某个对称轨道,同一条轨道上的点共享同一个权重,这样既保证了规则对坐标旋转不变,又大幅减少了待定参数的数量。
常见的轨道类型有几种:中心轨道(四面体重心,1 个点)、内点轨道(沿重心指向顶点的方向,一组 4 个点)、面内轨道(在某个面内偏离面中心,一组 6 个或 12 个点)、边轨道(沿棱方向布置,一组 6 个点)等。设计一个高精度规则,就是选取若干轨道,把每条轨道的权重作为未知数,再把轨道内的自由位置参数交给优化条件去决定。
例如 1 点规则只有重心一个点,要求对常数函数精确,权重直接确定为参考单元体积 1/6。4 点规则通常由重心加 3 个对称内点组成,能对二次多项式精确积分。5 点规则由重心加 4 个近顶点内点组成,能达到三次精度。这类规则的点位由对称性锁定,自由度不多,用手算就能校核。更高阶的 15 点规则则是中心点、面内轨道和边附近轨道组合的产物。
2.3 典型积分表:点分布、精度阶与使用注意
下表列出工程上最常见的几类四面体高斯积分规则的结构特征。注意这里只写点分布类型和宣称的精度,不写具体权重值,因为不同论文给出的数值略有差异,直接抄表之前必须做自检。
| 规则名称 | 点数量 | 宣称多项式精度 | 轨道构成 |
|---|---|---|---|
| 重心规则 | 1 | 1 次 | 中心轨道 |
| 四点规则 | 4 | 2 次 | 重心 + 3 个内点 |
| 五点规则 | 5 | 3 次 | 中心轨道(负权重)+ 4 个内点 |
| 十四点规则 | 14 | 5 次 | 中心 + 面内点 + 内点组合 |
| 十五点规则(Keast 型) | 15 | 5 次 | 中心 + 多组内点与面内点 |
注意:五点规则中中心点权重是负值是正常现象,不是表抄错了。负权重会给退化单元上的稳定性带来隐患,这一点在第 4 章专门展开。
权重有一个硬性校准条件:所有积分点的权重之和必须等于参考四面体的体积,也就是 1/6。如果抄来的表不满足这个条件,要么是归一化方式不同,要么就是表本身有误。拿到任何一张积分表,我第一件事就是算这个总和。
3. 从参考单元到物理四面体:映射、Jacobian 与最小可运行脚本
3.1 仿射映射与 Jacobian 的三种求法
实际单元不会是标准参考四面体,积分要通过仿射映射完成。设物理四面体顶点为 A、B、C、D,则映射是:
x(λ) = λ1·A + λ2·B + λ3·C + λ4·D
由于参考四面体的坐标是线性独立的,这个映射也是线性的,所以 Jacobian 矩阵 J 是常数矩阵,不会随积分点位置变化。计算 J 的常见做法是取三条从 A 出发的边向量作为列向量:
J = [B-A, C-A, D-A]
行列式 detJ 就是体积放大系数。因为参考四面体体积为 1/6,物理四面体体积则为 |detJ|/6,反过来也说明 detJ 的绝对值等于物理体积的 6 倍。
除了直接列边向量,也可以用形函数对坐标求导来构造 J,或直接算三个边长向量的混合积。三种方法数学上完全等价,实操中我推荐第一种,因为它不需要额外的形函数导数代码,出错概率最低。唯一的坑是顶点顺序不能反,顺序反了 detJ 为负,绝对值依然对,但面积分方向会出问题,后面避坑章再细说。
3.2 用 Python 在实体四面体上算积分:最小可运行脚本
下面这个脚本把一个四面体上的积分算子完整实现出来。为了演示原理,积分表先只用 1 点重心规则。
import numpy as np # 物理四面体顶点:边长 2 的直角四面体,体积 4/3 verts = np.array([ [0.0, 0.0, 0.0], [2.0, 0.0, 0.0], [0.0, 2.0, 0.0], [0.0, 0.0, 2.0] ], dtype=float) # 1 点规则:重心在体积坐标下是 (0.25, 0.25, 0.25, 0.25) # 权重等于参考四面体体积 1/6 rule = { "pts": np.array([[0.25, 0.25, 0.25, 0.25]]), "w": np.array([1.0 / 6.0]) } def tet_integrate(verts, f, rule): # 构造 Jacobian 矩阵,列向量为三条边向量 J = np.column_stack([ verts[1] - verts[0], verts[2] - verts[0], verts[3] - verts[0] ]) detJ = np.abs(np.linalg.det(J)) total = 0.0 for l, wi in zip(rule["pts"], rule["w"]): # 体积坐标线性组合得到物理坐标 xyz = (l[0] * verts[0] + l[1] * verts[1] + l[2] * verts[2] + l[3] * verts[3]) total += wi * f(*xyz) return total * detJ # 对常数 1 积分,结果应等于四面体体积 4/3 = 1.333... f1 = lambda x, y, z: 1.0 print("积分常数:", tet_integrate(verts, f1, rule)) # 对 x 积分,解析值为体积乘重心 x 坐标 = (4/3) * 0.5 f2 = lambda x, y, z: x print("积分 x:", tet_integrate(verts, f2, rule), "解析值:", 4.0 / 3.0 * 0.5)逻辑说明:函数先算 detJ,再遍历积分点。每个积分点的体积坐标通过线性组合换算成物理坐标,代入被积函数求值,乘上权重,最后统一乘 detJ。
参数说明:这里 detJ 算出来是 8。因为三个边向量分别是 (2,0,0)、(0,2,0)、(0,0,2),行列式为 8。参考单元上权重和是 1/6,乘 detJ 后得到 8/6 = 4/3,正好是体积。对线性函数 x,1 点规则是精确的,所以数值解与解析解完全一致。这是检验脚本有没有写错的最快方法,任何积分器都应该先过这一关。
3.3 参数含义与精度验证方法
detJ 是三个边向量叉积的模,它同时决定了积分结果的数量级。如果你的积分结果和解析解差着一个固定倍数,比如所有结果都是正确值的 6 倍,大概率是参考单元体积没除干净,权重总和不是 1/6 而是 1。这种情况常见于从六面体积分表迁移过来的代码:六面体参考单元体积是 1,四面体是 1/6,忘改权重就直接全错了。
验证积分器时,我一般按三个步骤走。先积分常数函数,验证结果等于单元体积;再积分线性函数,验证结果等于体积乘重心坐标的对应分量;最后积分一个二次多项式,比如 x²,与手算的解析值对比,这一步能暴露低阶规则是否被错误地用于高阶被积函数。这三个检查覆盖了权重总量、映射方向和多项式精度三个最容易出错的层面,总耗时不超过五分钟。
把高阶积分表替换进上面的脚本也很简单,只需要改 rule 字典里的 pts 和 w 两个数组,点的数量变了,主循环不用动。这也是把积分器封装成函数而不是散写在主程序里的原因:换表只是换数据,不碰逻辑。
4. 四面体高斯积分的避坑清单:从坐标顺序到权重总量
4.1 坑一:权重总量不等于参考单元体积
现象:用抄来的积分表算常数函数的积分,结果不是 1.333 而是 7.999,也就是正确体积的 6 倍。换了几个被积函数都一样,误差是固定倍数。
原因:积分点表按参考四面体单元定义,权重和应为 1/6。很多积分表来自通用数值积分手册,默认参考域是单位立方体或单位球面,权重和为 1。直接套用到四面体上,体积因子就差了 6 倍。
解决:拿到任何积分表先做校准,把所有权重加起来,确认等于 1/6。如果权重和等于 1,要么整体除以 6,要么在乘积步骤手动修正。我更建议在代码里写一个断言,启动时自动检查权重和,避免以后换表时再犯。
4.2 坑二:顶点顺序错误导致 Jacobian 为负
现象:对常数函数积分结果数值正确,但某个单元算出来的体力方向或面法向和预期相反,导致刚度矩阵不对称或装配结果异常。
原因:四面体顶点顺序没有按右手系排列。同一个四面体,交换两个顶点的位置,三个边向量构成的混合积会改变符号。detJ 取绝对值能保证积分量是正的,但单元的体积坐标映射方向已经反了,和相邻单元共享面时外法向就会对不上。
解决:在网格生成阶段统一顶点顺序约定,比如按右手螺旋规则排列。如果网格文件来自第三方工具,先写一个批量检查脚本,统计所有单元 detJ 的符号,出现负数单元就交换对应顶点重新编号。这个检查在四面体网格处理中是标准步骤,不能跳过。
4.3 坑三:负权重规则在退化单元上精度崩塌
现象:同样的积分规则,在规则四面体上精度正常,换到细长或扁平的网格单元上,二阶矩误差突然上升一个数量级,甚至出现负的体积积分。
原因:5 点规则和部分高阶规则包含负权重。数学上负权重保证了对高阶多项式在对称位置上的精度,但当单元形状退化时,积分点可能落到非常靠近边界甚至单元之外的位置,负权重与正权重之间的数值抵消变差,舍入误差被放大。
解决:对网格质量比较差的工业模型,优先选全正权重的规则。1 点、4 点和部分 15 点规则是全正权重,精度稍低但稳定得多。如果必须用 5 点以上规则,先对网格做质量检查,把体积与最大边长比过小的单元先做局部加密或重剖,再上高阶积分。
4.4 坑四:用六面体张量积冒充四面体规则
现象:积分点在四面体外,计算结果忽大忽小。检查单元内部场分布时,发现采样点根本没落在单元里。
原因:有人图省事,把 [-1,1]³ 上的高斯点做线性映射直接套到四面体上。四面体区域是单纯形,有三个坐标受 x+y+z≤1 约束,张量积点在高维正方体里均匀分布,映射到四面体后必然有大量点落在区域外。
解决:没有捷径,四面体积分规则必须按单纯形的对称群来构造。六面体规则和四面体规则之间不存在简单变换关系,不要尝试推导。直接使用本文第 2 章介绍的轨道构造法,或者从可信的积分规则库中获取四面体专用表。
4.5 坑五:积分表来源不明直接上生产
现象:新换的积分规则在简单单元上测试全过,在复杂装配体上出现局部能量不守恒,排查几天找不到原因。
原因:积分表是手工抄录的,某个点坐标的小数点后第四位抄错。对于对称性强的规则,单个点的小误差会被其他点掩盖,四个点以上的规则很难靠目测发现,只有对高阶多项式逐项验证才能暴露。
解决:所有积分表进入工程代码前,强制跑一遍多项式精度自检。脚本不复杂,就是循环对一组单项式求积分,对比解析值。这个习惯帮我挡掉过至少三张来源可疑的积分表,建议所有有限元框架都把自检写进单元测试,每次改动自动执行。
5. 积分阶数怎么选:从 P1 到 P3 单元的实际配点方案
5.1 刚度矩阵被积函数的阶数:一张表说清楚
选积分点数之前,先算清楚被积函数到底是多少阶多项式。以弹性力学为例,位移形函数是 p 阶多项式,应变是位移的一阶导数,因此应变为 p-1 阶;两个应变相乘得到刚度矩阵的被积函数,阶数是 2(p-1)。不同单元的阶数对应关系如下:
| 单元类型 | 位移阶数 p | 应变阶数 p-1 | 被积函数阶数 2(p-1) | 推荐积分规则 |
|---|---|---|---|---|
| P1 线性四面体 | 1 | 0 | 0 | 1 点规则即可 |
| P2 二次四面体 | 2 | 1 | 2 | 4 点规则或 5 点规则 |
| P3 三次四面体 | 3 | 2 | 4 | 5 点规则保守可用,推荐 15 点规则 |
| P4 四次四面体 | 4 | 3 | 6 | 15 点规则或更高 |
这张表的前三行覆盖了大多数工程场景。常有人误以为单元阶数越高积分点数必须跟着线性增长,实际被积函数阶数只跟应变有关,位移阶数提升一级,被积函数只提升两级,并不需要夸张的积分点数。
5.2 常见单元阶数的推荐配点方案
P1 单元被积函数是常数,理论上 1 点规则就完全精确。很多商业软件默认给 P1 单元配 4 点积分,不是精度需要,而是为了更准确地捕捉单元内材料非线性或温度分布,当材料参数随位置变化时,低阶规则会漏掉单元内场的变化信息。
P2 二次四面体单元的最常见配置是 4 点规则。这个规则对被积函数二次多项式完全精确,满足刚度矩阵积分需求。如果单元存在严重畸变,我会保守地换 5 点规则,多出来的点实际上起到改善采样分布的作用,而不是提高多项式精度。
P3 三次四面体单元建议直接上 15 点规则。P2 和 P3 单元的网格往往来自局部加密后的区域,单元质量波动大,15 点规则的全正权重特性比 5 点规则稳定得多。如果计算资源紧张,可以先用 5 点规则跑通流程,在收敛性验证阶段再切回 15 点对比。
注意:以上推荐针对线性弹性问题。涉及塑性、损伤、接触等非线性本构时,积分点同时是材料状态的存储点,积分点数不足会导致局部化现象被网格方向带偏。这时宁可多配积分点,也不要省。
5.3 用收敛阶实验验证积分规则是否够用
判断一套积分方案够不够用,最直接的方法是做一个简单的收敛阶实验。下面这个思路不需要额外网格工具:对一个剖分出来的四面体逐步细分,观测积分误差随单元尺寸减小的速度。如果误差下降斜率明显低于理论值,多半是积分规则配低了。
import numpy as np verts = np.array([ [0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0] ], dtype=float) f = lambda x, y, z: x**2 + y**2 + z**2 # 解析积分:对 x^2 积分 = 1/60,三方向对称,总计 1/20 exact = 0.05 def integrate_once(verts, nseg): # 简单实现:把边 [0,1] 切成 nseg 段,构造子四面体集合 # 这里仅演示思路,实际实现可以用递归八分或循环坐标偏移 total = 0.0 h = 1.0 / nseg # 直角四面体,按每个方向的小格子切分后取每个小四面体 for i in range(nseg): for j in range(nseg - i): for k in range(nseg - i - j): a = np.array([i*h, j*h, k*h], dtype=float) b = a + np.array([h, 0, 0], dtype=float) c = a + np.array([0, h, 0], dtype=float) d = a + np.array([0, 0, h], dtype=float) # 1 点规则:重心处求值,权重乘体积 g = (a + b + c + d) / 4.0 vol = h**3 / 6.0 total += f(*g) * vol return total for n in [1, 2, 4, 8]: approx = integrate_once(verts, n) err = abs(approx - exact) print(f"nseg={n}, 误差={err:.6e}")逻辑说明:这段代码把直角四面体按坐标方向切成若干小四面体,在每个小四面体的重心处用 1 点规则求值。因为被积函数是二次的,而 1 点规则只对线性函数精确,所以细分之后误差应该随 h 的平方减小。
参数说明:nseg 每翻一倍,单元尺寸 h 减半,理论误差下降到原来的四分之一。实际输出会看到误差按 4 倍左右的速度衰减。如果误差下降速度明显变慢,说明积分规则和被积函数阶数不匹配,或者网格剖分实现有问题。这个方法不依赖外部求解器,五分钟就能完成,值得固化为一套回归测试。
6. 把积分表当作代码库来管理:生成、自检与复用
6.1 用多项式精度条件反解权重:一个自检脚本
四面体积分规则的权威表散落在不同论文里,抄录易错,不如写一个自检函数统一验证。下面的脚本把多项式精度条件直接编码进去,给定任意积分表和指定精度阶数,自动判断是否达标。
import numpy as np from itertools import combinations_with_replacement def check_rule(rule, maxdeg=5): pts, w = rule # 参考四面体体积 1/6 vol_ref = 1.0 / 6.0 bad = [] for deg in range(maxdeg + 1): for comb in combinations_with_replacement(range(3), deg): monomial = np.zeros(3) for idx in comb: monomial[idx] += 1 # 参考单元上单项式的解析积分 # 积分公式: (a! b! c!) / (a+b+c+3)! exact = (np.math.factorial(int(monomial[0])) * np.math.factorial(int(monomial[1])) * np.math.factorial(int(monomial[2]))) / \ np.math.factorial(int(monomial.sum()) + 3) approx = 0.0 for l, wi in zip(pts, w): # 体积坐标到笛卡尔坐标参考单元: # x = l1, y = l2, z = l3, l4 = 1 - x - y - z x, y, z = l[0], l[1], l[2] approx += wi * (x**monomial[0] * y**monomial[1] * z**monomial[2]) if abs(approx - exact) > 1e-12: bad.append((deg, comb, exact, approx)) return bad # 用 1 点规则验证 rule_1pt = (np.array([[0.25, 0.25, 0.25, 0.25]]), np.array([1.0 / 6.0])) print("1 点规则不达标的单项式数量:", len(check_rule(rule_1pt, 2)))逻辑说明:脚本遍历直到 maxdeg 阶的所有单项式,计算每个单项式在参考单元上的解析积分与积分表近似值,存在任何误差超过阈值就记入 bad 列表。注意解析积分的公式用的是阶乘比值,这是单纯形上多项式积分的闭式解,不需要数值积分。
参数说明:参考单元上体积坐标与笛卡尔坐标的对应关系是 x=λ1、y=λ2、z=λ3。脚本对 1 点规则做二阶精度检查时,会发现 x² 项不达标,这正是预期行为,因为 1 点规则只保证线性精度。把规则替换成 4 点或 15 点表,bad 列表应为空。
6.2 集成到有限元主循环的规范做法
我习惯把所有积分表放在独立的常量模块里,每张表附一个精度阶数字段。刚度矩阵组装时,根据单元类型取出对应规则,走统一的分发接口。这样做的好处是换规则只改数据不动代码,排查问题时也能快速锁定是哪张表出问题。
工程质量层面的一个关键做法是在求解器启动阶段对所有已注册积分表执行一次精度自检,任何表不达标立即报错退出。配合上面这个 check_rule 函数,这个检查框架成本极低,却能在问题扩散到整个装配体之前拦住错误。多年下来我的教训是:积分表的坑从来不在打开文件那一刻暴露,而是在网格加密到某一轮时突然让计算结果翻车。把自检前置到启动阶段,是我个人最推荐的一道防线,希望帮到你。
本文还有配套的精品资源,点击获取