AI 求解偏微分方程:算子学习与神经算符
python import torch import torch.nn as nn import torch.fft
class SpectralConv2d(nn.Module): def init(self, in_channels, out_channels, modes1, modes2): super().init() self.in_channels = in_channels self.out_channels = out_channels self.modes1 = modes1 self.modes2 = modes2 self.scale = (1 / (in_channels * out_channels)) self.weights1 = nn.Parameter(self.scale * torch.rand(in_channels, out_channels, modes1, modes2, dtype=torch.cfloat)) self.weights2 = nn.Parameter(self.scale * torch.rand(in_channels, out_channels, modes1, modes2, dtype=torch.cfloat))
def forward(self, x):
batchsize = x.shape[0]
# 傅里叶变换
x_ft = torch.fft.rfft2(x)
out_ft = torch.zeros(batchsize, self.out_channels, x.size(-2), x.size(-1)//2 + 1, dtype=torch.cfloat, device=x.device)
# 低频截断与线性变换
out_ft[:, :, :self.modes1, :self.modes2] = torch.einsum("bixy,ioxy->boxy", x_ft[:, :, :self.modes1, :self.modes2], self.weights1)
out_ft[:, :, -self.modes1:, :self.modes2] = torch.einsum("bixy,ioxy->boxy", x_ft[:, :, -self.modes1:, :self.modes2], self.weights2)
x = torch.fft.irfft2(out_ft, s=(x.size(-2), x.size(-1)))
return x
class FNO2d(nn.Module): def init(self, modes1, modes2, width): super().init() self.fc0 = nn.Linear(3, width) # 输入通道:k场 + 网格坐标(可选) self.conv0 = SpectralConv2d(width, width, modes1, modes2) self.conv1 = SpectralConv2d(width, width, modes1, modes2) self.conv2 = SpectralConv2d(width, width, modes1, modes2) self.conv3 = SpectralConv2d(width, width, modes1, modes2) self.w0 = nn.Conv2d(width, width, 1) self.w1 = nn.Conv2d(width, width, 1) self.w2 = nn.Conv2d(width, width, 1) self.w3 = nn.Conv2d(width, width, 1) self.fc1 = nn.Linear(width, 128) self.fc2 = nn.Linear(128, 1)
def forward(self, k):
# k: (batch, 1, h, w)
batchsize, size1, size2 = k.shape[0], k.shape[-2], k.shape[-1]
# 添加网格坐标作为额外特征(提高位置感知)
grid = self.get_grid(batchsize, size1, size2, k.device)
x = torch.cat((k, grid), dim=1) # (batch, 3, h, w)
x = self.fc0(x.permute(0, 2, 3, 1)).permute(0, 3, 1, 2)
x1 = self.conv0(x)
x2 = self.w0(x)
x = x1 + x2
x = F.gelu(x)
# ... 重复添加更多层,与残差连接
x = self.fc1(x.permute(0, 2, 3, 1))
x = F.gelu(x)
x = self.fc2(x).permute(0, 3, 1, 2) # 输出u场 (batch, 1, h, w)
return x
def get_grid(self, batchsize, size1, size2, device):
gridx = torch.linspace(0, 1, size1)
gridy = torch.linspace(0, 1, size2)
gridx, gridy = torch.meshgrid(gridx, gridy, indexing='ij')
grid = torch.stack((gridx, gridy), dim=0).unsqueeze(0).repeat(batchsize, 1, 1, 1)
return grid.to(device)