CFD_Benchmark / model /DeepONet.py
OneScience's picture
Upload folder using huggingface_hub
ff0fadf verified
Raw
History Blame Contribute Delete
4.34 kB
import torch
import torch.nn as nn
from onescience.modules.mlp.MLP import StandardMLP
from onescience.modules.embedding import timestep_embedding, unified_pos_embedding
class Model(nn.Module):
"""
DeepONet 模型。
采用双网络架构 (Branch Net 和 Trunk Net) 逼近非线性算子。
- Branch Net 处理输入函数 (如初始条件、边界条件)。
- Trunk Net 处理目标评估点的坐标 (物理位置)。
两者输出特征做内积后得到目标位置的物理场预测值。
"""
def __init__(self, args, device):
super(Model, self).__init__()
self.__name__ = "DeepONet"
self.args = args
# 位置处理
if args.unified_pos and args.geotype != "unstructured":
self.pos = unified_pos_embedding(args.shapelist, args.ref, device=device)
trunk_in_dim = args.ref ** len(args.shapelist)
else:
trunk_in_dim = args.space_dim
# 分支网络输入维度(函数输入+时间嵌入)
branch_in_dim = args.fun_dim
if args.time_input:
branch_in_dim += args.n_hidden # 增加时间嵌入维度
# 分支网络(函数空间)
self.branch_net = StandardMLP(
input_dim=branch_in_dim,
output_dim=args.n_hidden,
hidden_dims=[args.n_hidden] * args.branch_depth,
activation=args.act,
use_bias=True
)
# 主干网络(物理空间)
self.trunk_net = StandardMLP(
input_dim=trunk_in_dim,
output_dim=args.n_hidden,
hidden_dims=[args.n_hidden] * args.trunk_depth,
activation=args.act,
use_bias=True
)
# 时间处理层
if args.time_input:
self.time_fc = nn.Sequential(
nn.Linear(args.n_hidden, args.n_hidden),
nn.SiLU(),
nn.Linear(args.n_hidden, args.n_hidden),
)
# 输出层 - 支持多个输出通道
self.out_layer = nn.Linear(args.n_hidden, args.out_dim)
self.bias = nn.Parameter(torch.zeros(1, 1, args.out_dim))
def structured_forward(self, x, fx, T=None, geo=None):
B, N, _ = x.shape # x: [B, N, space_dim]
if self.args.unified_pos:
x = self.pos.repeat(B, 1, 1) # [B, N, d]
# 处理时间信息
if T is not None and self.args.time_input:
# 确保时间输入维度正确 [B, 1] -> [B]
T = T.view(-1)
# 生成时间嵌入 [B, D]
T_emb = timestep_embedding(T, self.args.n_hidden)
# 通过时间处理层 [B, D] -> [B, D]
T_emb = self.time_fc(T_emb)
# 扩展时间嵌入以匹配空间点 [B, D] -> [B, N, D]
T_emb_expanded = T_emb.unsqueeze(1).expand(-1, N, -1)
# 将时间嵌入连接到函数输入
if fx is not None:
# [B, N, fun_dim] + [B, N, D] = [B, N, fun_dim + D]
fx = torch.cat([fx, T_emb_expanded], dim=-1)
else:
fx = T_emb_expanded # [B, N, D]
# 分支网络处理
branch_feat = self.branch_net(fx) # [B, N, D]
# 主干网络处理
trunk_feat = self.trunk_net(x) # [B, N, D]
# 点积操作 + 输出层
inner = branch_feat * trunk_feat
out = self.out_layer(inner) + self.bias
return out
def unstructured_forward(self, x, fx, T=None):
B, N, _ = x.shape
if T is not None and self.args.time_input:
T = T.view(-1)
T_emb = timestep_embedding(T, self.args.n_hidden)
T_emb = self.time_fc(T_emb)
T_emb_expanded = T_emb.unsqueeze(1).expand(-1, N, -1)
if fx is not None:
fx = torch.cat([fx, T_emb_expanded], dim=-1)
else:
fx = T_emb_expanded
branch_feat = self.branch_net(fx)
trunk_feat = self.trunk_net(x)
inner = branch_feat * trunk_feat
out = self.out_layer(inner) + self.bias
return out
def forward(self, x, fx, T=None, geo=None):
if self.args.geotype == "unstructured":
return self.unstructured_forward(x, fx, T)
else:
return self.structured_forward(x, fx, T)