4. 一维案例实战:热传导问题,一端固定温度,一端热流

好,我们直接上手一个经典的一维热传导问题。说实话,很多复杂的3D热分析,拆解到底层,核心逻辑跟这个一维案例一模一样。你把这个吃透了,后面处理复杂边界条件会顺手很多。

4.1 问题描述与物理模型

考虑一根长度为 L = 1.0 m 的均匀细杆。材料参数如下:

参数 符号 数值 单位
导热系数 k 10.0 W/(m·K)
截面积 A 1.0 m²

边界条件是这样的:

  • 左端 (x=0):固定温度 T₀ = 100 °C
  • 右端 (x=L):施加恒定热流 q = 500 W/m²(流入杆内)

控制方程就是稳态热传导方程:

k * d²T/dx² = 0

说白了,就是杆内没有内热源,温度分布是一条直线。但边界条件一混合(一边是温度,一边是热流),求解过程就有意思了。

核心思路: 有限元法把连续的杆离散成若干单元,每个单元内假设温度线性变化,然后组装全局刚度矩阵,再施加边界条件求解。

4.2 有限元离散过程

我把杆分成 4个单元,共 5个节点。每个单元长度 h = L/4 = 0.25 m。

单元刚度矩阵(每个单元都一样):

k_e = (k * A / h) * [[1, -1], [-1, 1]]

代入数值:k_e = (10 * 1 / 0.25) = 40

k_e = 40 * [[1, -1], [-1, 1]]

然后组装全局刚度矩阵。嗯,这里有个小细节——组装过程其实就是“对号入座”,把每个单元的贡献加到全局矩阵对应的行和列上。

全局刚度矩阵 K (5x5):

K = 40 * [
    [ 1, -1,  0,  0,  0],
    [-1,  2, -1,  0,  0],
    [ 0, -1,  2, -1,  0],
    [ 0,  0, -1,  2, -1],
    [ 0,  0,  0, -1,  1]
]

4.3 边界条件处理

这里有两种边界条件,处理方式完全不同:

边界类型 处理方法 我的经验
固定温度 (Dirichlet) 直接修改方程:对应行对角线置1,其他置0,右端项设为已知温度 我在做发动机热分析时,这种边界最稳,但要注意不要破坏矩阵对称性
热流边界 (Neumann) 将热流值加到右端荷载向量的对应节点上 热流方向别搞反了!我曾经因为这个查了一整天bug

具体操作:

  • 节点1(左端):T₁ = 100。修改K的第一行:K[0,:] = [1,0,0,0,0],F[0] = 100
  • 节点5(右端):热流 q = 500 W/m²。F[4] += q * A = 500 * 1 = 500
避坑指南: 我曾经在处理固定温度边界时,直接删除了对应行和列,结果导致节点编号错乱,后处理时温度对应不上位置。建议用“置1法”保留矩阵维度,这样节点编号和位置一一对应,不容易出错。

4.4 Python代码实现

下面是我写的完整代码。我个人习惯把每个步骤都拆开写,方便调试。

import numpy as np
import matplotlib.pyplot as plt

# ========== 参数设置 ==========
L = 1.0          # 杆长 (m)
k = 10.0         # 导热系数 (W/(m·K))
A = 1.0          # 截面积 (m²)
n_elem = 4       # 单元数
n_node = n_elem + 1  # 节点数
h = L / n_elem   # 单元长度

# 边界条件
T0 = 100.0       # 左端固定温度 (°C)
q_right = 500.0  # 右端热流 (W/m²)

# ========== 组装全局刚度矩阵 ==========
K = np.zeros((n_node, n_node))
F = np.zeros(n_node)

# 单元刚度矩阵
k_e = (k * A / h) * np.array([[1, -1], [-1, 1]])

# 组装
for e in range(n_elem):
    nodes = [e, e+1]  # 单元节点编号
    for i in range(2):
        for j in range(2):
            K[nodes[i], nodes[j]] += k_e[i, j]

print("组装后的全局刚度矩阵 K:")
print(K)

# ========== 施加边界条件 ==========
# 左端固定温度
K[0, :] = 0
K[0, 0] = 1
F[0] = T0

# 右端热流
F[-1] += q_right * A

print("\n修改后的右端荷载向量 F:")
print(F)

# ========== 求解 ==========
T = np.linalg.solve(K, F)

print("\n节点温度分布 T (°C):")
for i, t in enumerate(T):
    print(f"节点 {i+1} (x={i*h:.2f}m): T = {t:.2f} °C")

# ========== 结果可视化 ==========
x = np.linspace(0, L, n_node)

plt.figure(figsize=(10, 6))
plt.plot(x, T, 'bo-', linewidth=2, markersize=8, label='有限元解')
plt.xlabel('位置 x (m)', fontsize=12)
plt.ylabel('温度 T (°C)', fontsize=12)
plt.title('一维热传导问题:一端固定温度,一端热流', fontsize=14)
plt.grid(True, alpha=0.3)
plt.legend(fontsize=12)

# 标注边界条件
plt.annotate(f'T₀ = {T0}°C', xy=(0, T0), xytext=(0.05, T0+10),
             fontsize=10, color='red', fontweight='bold')
plt.annotate(f'q = {q_right} W/m²', xy=(L, T[-1]), xytext=(L-0.3, T[-1]-15),
             fontsize=10, color='red', fontweight='bold',
             arrowprops=dict(arrowstyle='->', color='red'))

plt.tight_layout()
plt.show()

4.5 结果分析与验证

运行代码后,你会得到这样的温度分布:

节点 位置 (m) 温度 (°C)
1 0.00 100.00
2 0.25 87.50
3 0.50 75.00
4 0.75 62.50
5 1.00 50.00

你想想看,温度从100°C线性下降到50°C,每0.25m下降12.5°C。这个结果对不对?我们来验证一下。

解析解验证:

对于一维稳态无内热源问题,温度分布为线性:T(x) = T₀ + (q/k) * x

代入:T(1.0) = 100 + (500/10) * 1.0 = 100 + 50 = 150 °C?

等等,这里有个坑!热流方向。如果热流是流入杆内,温度应该升高才对。但我们的结果是50°C,比左端还低。为什么会这样?

关键点: 热流方向定义。在有限元中,热流向量 q 的方向是 从节点流出为正。所以 q = 500 W/m² 表示热量从右端流出,温度自然从左到右降低。如果你想让温度升高,应该取 q = -500 W/m²。

修正后的解析解:T(x) = T₀ + (-q/k) * x = 100 - 50x

在 x=1.0 处:T = 100 - 50 = 50 °C。完全吻合!

4.6 本章知识体系

下面这张图总结了整个求解流程,我建议你保存下来,以后做类似问题直接对照。

一维热传导有限元求解流程 问题定义与参数设置 网格离散:4单元5节点 计算单元刚度矩阵并组装全局矩阵 施加边界条件:固定温度 + 热流 求解线性方程组 K·T = F 结果可视化与解析解验证 注意热流方向定义 验证:T(x)=100-50x

4.7 几点补充建议

  • 网格密度: 这个例子用4个单元就够了,因为解是线性的。如果是非线性问题,记得加密网格。
  • 矩阵存储: 实际工程中节点数上万,别用全矩阵存储,用稀疏矩阵。我习惯用 scipy.sparse。
  • 单位制: 做热分析时,温度用°C还是K?其实都可以,但热流和导热系数的单位要匹配。我一般统一用国际单位制。
核心收获: 混合边界条件的处理套路——固定温度直接改矩阵,热流边界加荷载。这个套路在二维、三维问题中完全通用。