| import math |
| import os |
| import torch |
| import sys |
| import numpy as np |
| import torch.nn as nn |
| from dataclasses import dataclass |
| from onescience.utils.YParams import YParams |
| from onescience.models.meta import ModelMetaData |
| from onescience.modules.embedding.xiheembedding import XiheEmbedding |
| from onescience.modules.fuser.xihefuse import XiheFuser |
| from onescience.modules.recovery.xihepatchrecovery import XihePatchRecovery |
| from onescience.modules.sample.pangudownsample import PanguDownSample |
| from onescience.modules.sample.xiheupsample import XiheUpSample |
|
|
|
|
|
|
| @dataclass |
| class MetaData(ModelMetaData): |
| name: str = "Xihe" |
| |
| jit: bool = False |
| cuda_graphs: bool = True |
| amp: bool = True |
| |
| |
| onnx_cpu: bool = False |
| onnx_gpu: bool = True |
| onnx_runtime: bool = True |
| |
| |
| var_dim: int = 1 |
| func_torch: bool = False |
| auto_grad: bool = False |
| |
| class TensorWithMask: |
| def __init__(self, x, mask): |
| self.x = x |
| self.mask = mask |
| self.y=None |
|
|
| |
| class Xihe(nn.Module): |
| """ |
| Xihe A PyTorch impl of: `XiHe: A Data-Driven Model for Global Ocean Eddy-Resolving Forecasting` |
| https://arxiv.org/abs/2402.02995 |
| """ |
| def __init__( |
| self, |
| config, |
| img_size=(2041, 4320), |
| patch_size=(6, 12), |
| window_size=(6, 12), |
| embed_dim=192, |
| num_heads=(6, 12, 12, 6), |
| in_chans=96, |
| depth=1, |
| mask_full=None, |
| out_chans=94, |
| num_groups=32, |
| |
| ): |
| super().__init__() |
| self.img_size = config.img_size |
| self.patch_size =config.patch_size |
| |
| self.mask=config.mask |
| mask_full = np.load(self.mask) |
| self.mask_full = mask_full if mask_full is not None else None |
| self.mask_h_w=None |
| self.out_chans=config.out_chans |
| self.in_chans=config.in_chans |
| self.num_groups=config.num_groups |
| self.embed_dim=config.embed_dim |
| |
| self.skip_proj = nn.Linear(2*self.embed_dim, self.embed_dim) |
|
|
|
|
| self.patchembed2d = XiheEmbedding() |
| self.patchrecovery2d = XihePatchRecovery() |
|
|
| |
| H_out = math.ceil(img_size[0] / patch_size[0]) |
| W_out = math.ceil(img_size[1] / patch_size[1]) |
| input_resolution = (1, H_out, W_out) |
| |
| self.mask_h_w=input_resolution |
| |
| window_size_3d = (1, window_size[0], window_size[1]) |
|
|
| |
| if depth > 1: |
| drop_path = np.linspace(0, 0.2, depth).tolist() |
| else: |
| drop_path = 0.0 |
|
|
| self.block1=XiheFuser(dim=self.embed_dim,input_resolution=input_resolution,num_local=1) |
|
|
| self.downsample = PanguDownSample( |
| in_dim=self.embed_dim, |
| input_resolution=(H_out, W_out), |
| output_resolution=(H_out // 2, W_out // 2)) |
| |
| input_resolution = (1, H_out // 2, W_out // 2) |
| self.mask_h_w=input_resolution |
|
|
| self.block2=XiheFuser(dim=2*self.embed_dim,input_resolution=input_resolution,num_local=2) |
|
|
| self.block3=XiheFuser(dim=2*self.embed_dim,input_resolution=input_resolution,num_local=2) |
| self.block4=XiheFuser(dim=2*self.embed_dim,input_resolution=input_resolution,num_local=2) |
|
|
| self.upsample = XiheUpSample(in_dim=2*self.embed_dim,out_dim=embed_dim,input_resolution=(H_out // 2, W_out // 2), output_resolution=(H_out, W_out), ) |
| input_resolution = (1, H_out, W_out) |
| self.block5=XiheFuser(dim=self.embed_dim,input_resolution=input_resolution,num_local=1) |
| def change_mask(self,mask_full, x, h_out, w_out): |
| |
| if not torch.is_tensor(mask_full): |
| mask_full = torch.tensor(mask_full, dtype=torch.float32) |
| else: |
| mask_full = mask_full |
|
|
| H, W = mask_full.shape |
| patch_h = math.ceil(H / h_out) |
| patch_w = math.ceil(W / w_out) |
|
|
| mask_coarse = torch.zeros((h_out, w_out), dtype=torch.float32) |
| for i in range(h_out): |
| for j in range(w_out): |
| h0, h1 = i * patch_h, min((i + 1) * patch_h, H) |
| w0, w1 = j * patch_w, min((j + 1) * patch_w, W) |
| patch = mask_full[h0:h1, w0:w1] |
| mask_coarse[i, j] = 1.0 if torch.any(patch > 0.5) else 0.0 |
| |
| mask_coarse = mask_coarse.to(x.device, dtype=x.dtype) |
| B = x.shape[0] |
| mask_coarse = mask_coarse.unsqueeze(0).unsqueeze(0).repeat(B, 1, 1, 1) |
| return mask_coarse |
| |
| def forward(self, x: torch.Tensor): |
| x = self.patchembed2d(x) |
| x = x.flatten(2).transpose(1, 2) |
| B, N, C = x.shape |
| mask_full=self.mask_full |
| |
| |
| if mask_full is not None: |
| H_out = math.ceil(self.img_size[0] / self.patch_size[0]) |
| W_out = math.ceil(self.img_size[1] / self.patch_size[1]) |
| mask1 = self.change_mask(mask_full, x, h_out=H_out, w_out=W_out) |
| else: |
| mask1 = None |
| |
| obj1 = TensorWithMask(x, mask1) |
| x=self.block1(obj1) |
| x1=x |
| x=self.downsample(x) |
| |
| if mask_full is not None: |
| _,H_out,W_out = self.mask_h_w |
| mask2 = self.change_mask(mask_full, x, h_out=H_out, w_out=W_out) |
| else: |
| mask2 = None |
| obj2 = TensorWithMask(x, mask2) |
| x=self.block2(obj2) |
| obj2 = TensorWithMask(x, mask2) |
| x=self.block3(obj2) |
| obj2 = TensorWithMask(x, mask2) |
| x=self.block4(obj2) |
| x=self.upsample(x) |
| obj1 = TensorWithMask(x, mask1) |
| x=self.block5(obj1) |
| x_out = torch.cat([x, x1], dim=-1) |
| x_out = self.skip_proj(x_out) |
| |
| |
| H_ = math.ceil(self.img_size[0] / self.patch_size[0]) |
| W_ = math.ceil(self.img_size[1] / self.patch_size[1]) |
| x_out = x_out.transpose(1, 2).reshape(B, C, H_, W_) |
| x=self.patchrecovery2d(x_out) |
| return x |
|
|