3. 瞬态热-力耦合分析
任务目标
构建一个瞬态热-力耦合的PINN模型:结构受到随时间变化的热载荷作用,产生温度场与位移场耦合演化。模型将时间 t 作为额外输入维度,同时输出温度 T(x,t) 和位移 u(x,t),并满足热传导方程与热弹性平衡方程的联合约束。
物理模型与方程
考虑一维杆(长度 L=1),左端固定,右端自由。热载荷以边界条件形式施加:左端温度恒定 T(0,t)=1,右端绝热 ∂T/∂x(L,t)=0。初始温度 T(x,0)=0。热膨胀引起位移,控制方程为:
- 热传导方程:
∂T/∂t = α · ∂²T/∂x² - 热弹性平衡方程(准静态,忽略惯性项):
∂/∂x [E·(∂u/∂x - β·T)] = 0
其中 α=0.1(热扩散系数),E=1(杨氏模量),β=0.01(热膨胀系数)。边界条件:u(0,t)=0(固定端),∂u/∂x(L,t)=β·T(L,t)(自由端无外力)。
PINN网络结构与损失函数
网络输入为 (x, t),输出为 (T, u)。总损失由四部分组成:
- 热方程残差:
L_heat = MSE(∂T/∂t - α·∂²T/∂x²),在内部配点处计算 - 力平衡残差:
L_stress = MSE(∂/∂x(E·(∂u/∂x - β·T))),在内部配点处计算 - 边界与初始条件:温度边界/初始条件、位移固定端条件
- 自由端应力条件:
∂u/∂x(L,t) - β·T(L,t) = 0
关键完整代码
import torch
import torch.nn as nn
import numpy as np
# 物理参数
alpha = 0.1
E = 1.0
beta = 0.01
L = 1.0
# 网络定义
class HeatStressPINN(nn.Module):
def __init__(self):
super().__init__()
self.net = nn.Sequential(
nn.Linear(2, 40), nn.Tanh(),
nn.Linear(40, 40), nn.Tanh(),
nn.Linear(40, 40), nn.Tanh(),
nn.Linear(40, 2) # 输出 [T, u]
)
def forward(self, x, t):
inp = torch.cat([x, t], dim=1)
out = self.net(inp)
return out[:, 0:1], out[:, 1:2] # T, u
# 自动微分辅助函数
def gradients(y, x, order=1):
if order == 1:
return torch.autograd.grad(y, x, grad_outputs=torch.ones_like(y),
create_graph=True, retain_graph=True)[0]
else:
dy = torch.autograd.grad(y, x, grad_outputs=torch.ones_like(y),
create_graph=True, retain_graph=True)[0]
return torch.autograd.grad(dy, x, grad_outputs=torch.ones_like(dy),
create_graph=True, retain_graph=True)[0]
# 训练数据生成
def generate_data(n_interior=2000, n_boundary=200, n_initial=200):
# 内部配点
x_int = torch.rand(n_interior, 1) * L
t_int = torch.rand(n_interior, 1) * 2.0
x_int.requires_grad_(True)
t_int.requires_grad_(True)
# 边界点:x=0 和 x=L
t_b = torch.rand(n_boundary, 1) * 2.0
x_left = torch.zeros_like(t_b)
x_right = torch.ones_like(t_b) * L
# 初始点:t=0
x_init = torch.rand(n_initial, 1) * L
t_init = torch.zeros_like(x_init)
return (x_int, t_int), (x_left, x_right, t_b), (x_init, t_init)
# 损失计算
def compute_loss(model, data):
(x_int, t_int), (x_left, x_right, t_b), (x_init, t_init) = data
# 内部配点:热方程 + 力平衡
T_int, u_int = model(x_int, t_int)
dT_dt = gradients(T_int, t_int)
d2T_dx2 = gradients(T_int, x_int, order=2)
heat_res = dT_dt - alpha * d2T_dx2
du_dx = gradients(u_int, x_int)
d2u_dx2 = gradients(u_int, x_int, order=2)
dT_dx = gradients(T_int, x_int)
stress_res = E * (d2u_dx2 - beta * dT_dx)
loss_heat = torch.mean(heat_res**2)
loss_stress = torch.mean(stress_res**2)
# 边界条件
T_left, u_left = model(x_left, t_b)
T_right, u_right = model(x_right, t_b)
# 左端温度 T(0,t)=1
loss_T_left = torch.mean((T_left - 1.0)**2)
# 右端绝热 dT/dx(L,t)=0
dT_dx_right = gradients(T_right, x_right)
loss_T_right = torch.mean(dT_dx_right**2)
# 左端固定 u(0,t)=0
loss_u_left = torch.mean(u_left**2)
# 右端自由 du/dx(L,t)=beta*T(L,t)
du_dx_right = gradients(u_right, x_right)
loss_u_right = torch.mean((du_dx_right - beta * T_right)**2)
# 初始条件 T(x,0)=0
T_init, _ = model(x_init, t_init)
loss_initial = torch.mean(T_init**2)
total_loss = (loss_heat + loss_stress +
loss_T_left + loss_T_right +
loss_u_left + loss_u_right + loss_initial)
return total_loss
# 训练循环
model = HeatStressPINN()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
data = generate_data()
print("开始训练瞬态热-力耦合PINN...")
for epoch in range(5000):
optimizer.zero_grad()
loss = compute_loss(model, data)
loss.backward()
optimizer.step()
if epoch % 500 == 0:
print(f"Epoch {epoch}, Loss: {loss.item():.6f}")
# 保存模型
torch.save(model.state_dict(), "thermal_stress_pinn.pth")
print("训练完成,模型已保存")
运行与验证
环境依赖:Python 3.8+,PyTorch 1.12+,NumPy。
运行方式:将上述代码保存为 thermal_stress_pinn.py,直接执行 python thermal_stress_pinn.py。
验证方法:训练完成后,可通过以下方式验证模型正确性:
- 损失收敛:训练日志中总损失应持续下降至
1e-4以下。 - 物理一致性检查:在
x=0处,模型预测温度应接近1;在t=0时,全场温度应接近0。 - 稳态对比:取较大时间(如
t=5),温度场应趋近线性分布T(x)=1-x/L(由边界条件T(0)=1、绝热右端推导),位移场应满足u(x)=β·(x - x²/(2L))的解析解形式。
通过标准:当总损失低于 1e-4,且上述物理一致性检查的相对误差在5%以内,即可判定模型训练成功。
常见问题
- 训练不收敛:尝试增加网络宽度(如从40改为60)或调整学习率至
1e-4。 - 位移场精度低于温度场:力平衡方程是二阶导数约束,可适当增加内部配点数量或对位移输出使用更高阶的激活函数。
- 时间范围选择:本案例取
t∈[0,2],若需更长时间演化,应相应增加训练配点密度。