标题六面体体积
这段代码在干一件非常基础但至关重要的事:计算每个网格格子的体积。
在流体力学模拟中,空间被切分成很多小格子(网格)。要计算格子里有多少气体、能量怎么变化,首先必须知道每个格子有多大(体积)。
由于网格可能是扭曲的(比如贴合飞机机翼的曲面网格),格子不是标准的正方体,不能直接用 长×宽×高 计算。这段代码用几何分解法来精确计算任意形状六面体的体积。
```python
#********************************************************************
# calculate volume of cells
#********************************************************************
@ti.kernel
def CVOL():
VOLMIN[None] = 1.0E7 # 初始化最小体积为一个极大值
# 遍历所有内部网格单元 (1 to NX)
for I, J, K in ti.ndrange((1, NX + 1), (1, NY + 1), (1, NZ + 1)):
# --- 计算辅助向量 SI (基于面 J方向) ---
# 这里的逻辑是将网格单元分割成四面体,利用向量叉乘计算面积分量
DX1 = XYZ[I, J + 1, K + 1][0] - XYZ[I, J, K][0]
DY1 = XYZ[I, J + 1, K + 1][1] - XYZ[I, J, K][1]
DZ1 = XYZ[I, J + 1, K + 1][2] - XYZ[I, J, K][2]
DX2 = XYZ[I, J, K + 1][0] - XYZ[I, J + 1, K][0]
DY2 = XYZ[I, J, K + 1][1] - XYZ[I, J + 1, K][1]
DZ2 = XYZ[I, J, K + 1][2] - XYZ[I, J + 1, K][2]
# 叉乘得到面积向量分量
SIX = 0.5 * (DY1 * DZ2 - DZ1 * DY2)
SIY = 0.5 * (DZ1 * DX2 - DX1 * DZ2)
SIZ = 0.5 * (DX1 * DY2 - DY1 * DX2)
# --- 计算辅助向量 SJ (基于面 I方向) ---
DX1 = XYZ[I + 1, J, K + 1][0] - XYZ[I, J, K][0]
DY1 = XYZ[I + 1, J, K + 1][1] - XYZ[I, J, K][1]
DZ1 = XYZ[I + 1, J, K + 1][2] - XYZ[I, J, K][2]
DX2 = XYZ[I + 1, J, K][0] - XYZ[I, J, K + 1][0]
DY2 = XYZ[I + 1, J, K][1] - XYZ[I, J, K + 1][1]
DZ2 = XYZ[I + 1, J, K][2] - XYZ[I, J, K + 1][2]
SJX = 0.5 * (DY1 * DZ2 - DZ1 * DY2)
SJY = 0.5 * (DZ1 * DX2 - DX1 * DZ2)
SJZ = 0.5 * (DX1 * DY2 - DY1 * DX2)
# --- 计算辅助向量 SK (基于面 K方向) ---
DX1 = XYZ[I + 1, J + 1, K][0] - XYZ[I, J, K][0]
DY1 = XYZ[I + 1, J + 1, K][1] - XYZ[I, J, K][1]
DZ1 = XYZ[I + 1, J + 1, K][2] - XYZ[I, J, K][2]
DX2 = XYZ[I, J + 1, K][0] - XYZ[I + 1, J, K][0]
DY2 = XYZ[I, J + 1, K][1] - XYZ[I + 1, J, K][1]
DZ2 = XYZ[I, J + 1, K][2] - XYZ[I + 1, J, K][2]
SKX = 0.5 * (DY1 * DZ2 - DZ1 * DY2)
SKY = 0.5 * (DZ1 * DX2 - DX1 * DZ2)
SKZ = 0.5 * (DX1 * DY2 - DY1 * DX2)
# --- 汇总面积分量 ---
SX = SIX + SJX + SKX
SY = SIY + SJY + SKY
SZ = SIZ + SJZ + SKZ
# --- 计算体积 ---
# 利用体积分解算法:V = (对角向量 · 总面积向量) / 3
XT1 = XYZ[I + 1, J + 1, K + 1][0] - XYZ[I, J, K][0]
YT1 = XYZ[I + 1, J + 1, K + 1][1] - XYZ[I, J, K][1]
ZT1 = XYZ[I + 1, J + 1, K + 1][2] - XYZ[I, J, K][2]
VOL[I, J, K] = (XT1 * SX + YT1 * SY + ZT1 * SZ) / 3.0
# 容错检查:体积不能为负或零
if VOL[I, J, K] <= 0.0:
out.write('NEGATIVE VOLUME')
out.write('I,J,K',I,J,K)
exit
# 更新全场最小体积
if VOL[I, J, K] <= VOLMIN[None]:
VOLMIN[None] = VOL[I, J, K]
---
## 🧊 一、核心目标:算一个"歪箱子"的体积
每个网格单元是一个**六面体**(8个顶点,像被捏变形的魔方):
(I+1,J+1,K+1) ________ (I,J+1,K+1)
/| /|
/ | / |
(I+1,J,K+1) /__|____/ |
| | (I+1,J+1,K) | |
| | / | | | (I,J,K+1)
| |/ | | | /
| /_____|____|___|/
(I+1,J,K) (I,J,K) (I,J+1,K)
**问题**:这个箱子是歪的,怎么算体积?
**答案**:把它切成几个**四面体**(三角锥),分别算体积再相加。
---
## 🔧 二、代码三步走
### 第1步:计算三个方向的"面积向量" (SI, SJ, SK)
代码里分了三大段,分别计算 `SI`、`SJ`、`SK`。
**通俗理解**:
想象你站在格子的一个角 `(I,J,K)`,看向三个相邻的面:
- **SI**:J-K 平面的对角面(类似"侧面")
- **SJ**:I-K 平面的对角面(类似"正面")
- **SK**:I-J 平面的对角面(类似"底面")
**计算方法**(以 SI 为例):
```python
# 向量1:从 (I,J,K) 到 (I,J+1,K+1) 的对角线
DX1 = XYZ[I, J+1, K+1] - XYZ[I, J, K]
# 向量2:从 (I,J+1,K) 到 (I,J,K+1) 的另一条对角线
DX2 = XYZ[I, J, K+1] - XYZ[I, J+1, K]
# 叉乘 → 得到垂直于这个面的"面积向量"
SIX = 0.5 * (DY1*DZ2 - DZ1*DY2) # X方向分量
SIY = 0.5 * (DZ1*DX2 - DX1*DZ2) # Y方向分量
SIZ = 0.5 * (DX1*DY2 - DY1*DX2) # Z方向分量
为什么叉乘?
- 两个向量的叉乘 = 它们围成的平行四边形的面积
- 乘以
0.5= 变成三角形的面积 - 结果是一个向量,方向垂直于这个面
第2步:把三个面积向量加起来
python
SX = SIX + SJX + SKX
SY = SIY + SJY + SKY
SZ = SIZ + SJZ + SKZ
通俗理解 :
把三个方向的"面"拼起来,形成一个包围盒的总表面积向量。
第3步:用"对角线·面积"算体积
python
# 主对角线:从 (I,J,K) 到 (I+1,J+1,K+1)
XT1 = XYZ[I+1, J+1, K+1][0] - XYZ[I, J, K][0]
YT1 = XYZ[I+1, J+1, K+1][1] - XYZ[I, J, K][1]
ZT1 = XYZ[I+1, J+1, K+1][2] - XYZ[I, J, K][2]
# 体积 = (对角线向量 · 总面积向量) / 3
VOL[I, J, K] = (XT1*SX + YT1*SY + ZT1*SZ) / 3.0
为什么除以3?
- 这是四面体体积公式 :V=13×底面积×高V = \frac{1}{3} \times \text{底面积} \times \text{高}V=31×底面积×高
- 点乘
(XT1*SX + ...)相当于算"高" - 整个六面体被分解成了多个四面体,最终等效为这个公式
📐 三、几何原理图解
顶点7
/| /|
/ | / |
顶点3---顶点6
| / | / |
| / | / 顶点5
|/ |/ /
顶点0---顶点4
/
顶点1,2 (隐藏)
六面体体积 = 5个四面体体积之和
= (对角线 · 面积向量) / 3
⚠️ 四、容错检查
python
if VOL[I, J, K] <= 0.0:
out.write('NEGATIVE VOLUME')
exit
为什么检查?
- 如果网格画得太扭曲,顶点顺序错了,体积可能算出负数
- 体积为负或零 → 网格无效 → 程序必须停止,否则后续计算全错
python
if VOL[I, J, K] <= VOLMIN[None]:
VOLMIN[None] = VOL[I, J, K]
记录最小体积:
- 用于后续判断时间步长(体积越小,时间步长要越短,否则计算不稳定)
🎯 五、一句话总结
这段代码把每个扭曲的六面体网格切成多个四面体,用向量叉乘和点乘的几何方法,精确计算每个格子的体积,并检查网格质量是否合格。
📊 类比帮助理解
| 代码操作 | 现实生活类比 |
|---|---|
XYZ[I,J,K] |
测量房间8个角的坐标 |
| 向量叉乘 | 用两根尺子量墙面的面积 |
| 向量点乘 | 量房间的"高度" |
| 除以3 | 三角锥体积公式 |
| 负体积检查 | 检查房间是不是"翻面"了(建模错误) |
| 记录最小体积 | 找到最小的房间,决定空调功率 |