Download model/massconservingcnn.py from OneScience-Group/MassConservingCNN: direct link, hf CLI and curl.
- Browser
- Download file 2.09 kB
-
https://huggingface.co/OneScience-Group/MassConservingCNN/resolve/main/model/massconservingcnn.py
- Command line
-
hf download hf://OneScience-Group/MassConservingCNN/model/massconservingcnn.py
-
curl -L -o massconservingcnn.py https://huggingface.co/OneScience-Group/MassConservingCNN/resolve/main/model/massconservingcnn.py
2.09 kB
| """Periodic one-dimensional CNN for mass-aware data-assimilation correction.""" | |
| from __future__ import annotations | |
| import torch | |
| from torch import nn | |
| from torch.nn import functional as F | |
| class PeriodicConv1d(nn.Module): | |
| """Conv1d with explicit circular padding and unchanged spatial length.""" | |
| def __init__(self, in_channels: int, out_channels: int, kernel_size: int): | |
| super().__init__() | |
| if kernel_size % 2 != 1: | |
| raise ValueError("kernel_size must be odd") | |
| self.pad = kernel_size // 2 | |
| self.conv = nn.Conv1d(in_channels, out_channels, kernel_size, padding=0) | |
| def forward(self, inputs: torch.Tensor) -> torch.Tensor: | |
| return self.conv(F.pad(inputs, (self.pad, self.pad), mode="circular")) | |
| class MassConservingCNN(nn.Module): | |
| """Four hidden SELU convolutions followed by the u/h/r output layer.""" | |
| def __init__(self, input_channels: int = 4, hidden_channels: int = 32, | |
| hidden_layers: int = 4, kernel_size: int = 3): | |
| super().__init__() | |
| if input_channels != 4 or hidden_layers != 4 or kernel_size != 3: | |
| raise ValueError("paper architecture requires 4 inputs, 4 hidden layers, kernel size 3") | |
| layers = [] | |
| channels = input_channels | |
| for _ in range(hidden_layers): | |
| layers.extend((PeriodicConv1d(channels, hidden_channels, kernel_size), nn.SELU())) | |
| channels = hidden_channels | |
| self.hidden = nn.Sequential(*layers) | |
| self.output = PeriodicConv1d(hidden_channels, 3, kernel_size) | |
| def influence_radius(self) -> int: | |
| return 5 | |
| def forward(self, inputs: torch.Tensor) -> torch.Tensor: | |
| if inputs.ndim != 3 or inputs.shape[1] != 4 or inputs.shape[2] != 250: | |
| raise ValueError(f"expected float tensor [B,4,250], got {tuple(inputs.shape)}") | |
| if not inputs.is_floating_point(): | |
| raise TypeError("inputs must have a floating-point dtype") | |
| raw = self.output(self.hidden(inputs)) | |
| return torch.cat((raw[:, :2], F.relu(raw[:, 2:3])), dim=1) | |