| 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 |
| if self.args.unified_pos: |
| x = self.pos.repeat(B, 1, 1) |
|
|
| |
| 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 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) |
|
|