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)。总损失由四部分组成:

  1. 热方程残差:L_heat = MSE(∂T/∂t - α·∂²T/∂x²),在内部配点处计算
  2. 力平衡残差:L_stress = MSE(∂/∂x(E·(∂u/∂x - β·T))),在内部配点处计算
  3. 边界与初始条件:温度边界/初始条件、位移固定端条件
  4. 自由端应力条件:∂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。

验证方法:训练完成后,可通过以下方式验证模型正确性:

  1. 损失收敛:训练日志中总损失应持续下降至 1e-4 以下。
  2. 物理一致性检查:在 x=0 处,模型预测温度应接近1;在 t=0 时,全场温度应接近0。
  3. 稳态对比:取较大时间(如 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],若需更长时间演化,应相应增加训练配点密度。