Download model/VINO_Darcy.py from OneScience-Group/VINO-DarcyFlow: direct link, hf CLI and curl.
- Browser
- Download file 3.68 kB
-
https://huggingface.co/OneScience-Group/VINO-DarcyFlow/resolve/main/model/VINO_Darcy.py
- Command line
-
hf download hf://OneScience-Group/VINO-DarcyFlow/model/VINO_Darcy.py
-
curl -L -o VINO_Darcy.py https://huggingface.co/OneScience-Group/VINO-DarcyFlow/resolve/main/model/VINO_Darcy.py
3.68 kB
| """Independently implemented Fourier operator with exact Darcy boundary values.""" | |
| from __future__ import annotations | |
| import torch | |
| from torch import nn | |
| from torch.nn import functional as F | |
| class FourierKernel(nn.Module): | |
| def __init__(self, width: int, modes: int): | |
| super().__init__() | |
| self.modes = modes | |
| self.upper = nn.Parameter(torch.rand(width, width, modes, modes, dtype=torch.complex64) / width**2) | |
| self.lower = nn.Parameter(torch.rand(width, width, modes, modes, dtype=torch.complex64) / width**2) | |
| def forward(self, field: torch.Tensor) -> torch.Tensor: | |
| spectrum = torch.fft.rfft2(field) | |
| m = self.modes | |
| if 2 * m > field.shape[-2] or m > spectrum.shape[-1]: | |
| raise ValueError("Fourier truncation exceeds the input resolution") | |
| # Contract channels at each retained frequency, with batch broadcasting. | |
| result = torch.zeros_like(spectrum) | |
| for band, weights in ((slice(0, m), self.upper), (slice(-m, None), self.lower)): | |
| coefficients = spectrum[:, :, band, :m].permute(2, 3, 0, 1) | |
| result[:, :, band, :m] = torch.matmul( | |
| coefficients, weights.permute(2, 3, 0, 1) | |
| ).permute(2, 3, 0, 1) | |
| return torch.fft.irfft2(result, s=field.shape[-2:]) | |
| class VINODarcy(nn.Module): | |
| """Map nodal permeability (B,H,W,1) to hydraulic head on [0,1]^2. | |
| Architecture details absent from the paper are documented in | |
| results/reproduction_spec.md. No solution labels enter this module. | |
| """ | |
| def __init__(self, modes: int = 12, width: int = 32, depth: int = 4, | |
| projection_width: int = 128, input_range=(-1.0, 1.0)): | |
| super().__init__() | |
| self.input_range = tuple(input_range) | |
| self.lift = nn.Linear(3, width) | |
| self.kernels = nn.ModuleList(FourierKernel(width, modes) for _ in range(depth)) | |
| self.spectral_maps = nn.ModuleList( | |
| nn.Sequential(nn.Conv2d(width, width, 1), nn.GELU(), nn.Conv2d(width, width, 1)) | |
| for _ in range(depth) | |
| ) | |
| self.local_maps = nn.ModuleList(nn.Conv2d(width, width, 1) for _ in range(depth)) | |
| self.project = nn.Sequential(nn.Linear(width, projection_width), nn.GELU(), nn.Linear(projection_width, 1)) | |
| self.register_buffer("grid", torch.empty(0), persistent=False) | |
| def forward(self, permeability: torch.Tensor) -> torch.Tensor: | |
| if permeability.ndim != 4 or permeability.shape[-1] != 1: | |
| raise ValueError("VINODarcy expects permeability with shape (B,H,W,1)") | |
| batch, height, width, _ = permeability.shape | |
| if self.grid.shape != (1, height, width, 2) or self.grid.device != permeability.device: | |
| x = torch.linspace(0, 1, height, device=permeability.device, dtype=permeability.dtype) | |
| y = torch.linspace(0, 1, width, device=permeability.device, dtype=permeability.dtype) | |
| self.grid = torch.stack(torch.meshgrid(x, y, indexing="ij"), dim=-1).unsqueeze(0) | |
| coordinates = self.grid.expand(batch, -1, -1, -1) | |
| lo, hi = self.input_range | |
| features = torch.cat(((2 * permeability - hi - lo) / (hi - lo), coordinates), dim=-1) | |
| hidden = self.lift(features).movedim(-1, 1) | |
| for index, (kernel, spectral_map, local_map) in enumerate(zip(self.kernels, self.spectral_maps, self.local_maps)): | |
| hidden = spectral_map(kernel(hidden)) + local_map(hidden) | |
| if index + 1 < len(self.kernels): | |
| hidden = F.gelu(hidden) | |
| raw = self.project(hidden.movedim(1, -1)) | |
| x, y = coordinates[..., 0:1], coordinates[..., 1:2] | |
| return raw * (x * (1 - x) * y * (1 - y)) | |