副产品技法

标题六面体体积

这段代码在干一件非常基础但至关重要的事:计算每个网格格子的体积

在流体力学模拟中,空间被切分成很多小格子(网格)。要计算格子里有多少气体、能量怎么变化,首先必须知道每个格子有多大(体积)

由于网格可能是扭曲的(比如贴合飞机机翼的曲面网格),格子不是标准的正方体,不能直接用 长×宽×高 计算。这段代码用几何分解法来精确计算任意形状六面体的体积。

复制代码
```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 三角锥体积公式
负体积检查 检查房间是不是"翻面"了(建模错误)
记录最小体积 找到最小的房间,决定空调功率
相关推荐
!停2 小时前
数据结构二叉树—链式结构(下)
数据结构·算法
逆境不可逃2 小时前
LeetCode 热题 100 之 41.缺失的第一个正数
算法·leetcode·职场和发展
码上发达2 小时前
状态压缩搜索解法(DFS + Dominance)
算法
颜酱2 小时前
差分数组:高效处理数组区间批量更新的核心技巧
javascript·后端·算法
yyy(十一月限定版)3 小时前
图论——最小生成树Kruskal算法
算法·图论
宇木灵3 小时前
C语言基础-十一、递归与分治(完结)
c语言·开发语言·学习·算法
We་ct3 小时前
LeetCode 173. 二叉搜索树迭代器:BSTIterator类 实现与解析
前端·算法·leetcode·typescript
weixin_395448913 小时前
main.c_0222cursor
c语言·前端·算法
Zik----4 小时前
Leetcode27 —— 移除元素(双指针)
数据结构·算法