| import torch |
| import numpy as np |
| from torch.utils.data import Dataset |
| from scipy.interpolate import griddata |
| from scipy.linalg import qr |
|
|
| class CFDReconstructionDataset(Dataset): |
| def __init__(self, unsteady_path, mean_path, sensor_count=65, target_grid=(128, 256), dt=0.01): |
| super().__init__() |
| print("[Dataset] Initializing v3.0 (Sparse Mode: 5 returns)...") |
| self.sensor_count = sensor_count |
| self.H, self.W = target_grid |
| self.dt = dt |
| |
| try: |
| raw_unsteady = np.load(unsteady_path).astype(np.float32) |
| raw_mean = np.load(mean_path).astype(np.float32) |
| except Exception as e: |
| print(f"Error loading data: {e}") |
| raise |
|
|
| mean_cols = raw_mean.shape[1] if raw_mean.ndim > 1 else 1 |
| coords_mean = raw_mean[:, :2] |
| x_min, y_min = coords_mean.min(axis=0) |
| x_max, y_max = coords_mean.max(axis=0) |
| self.box_len = [x_max - x_min, y_max - y_min, 1.0] |
| |
| grid_x = np.linspace(x_min, x_max, self.W) |
| grid_y = np.linspace(y_min, y_max, self.H) |
| self.grid_X, self.grid_Y = np.meshgrid(grid_x, grid_y) |
| self.grid_coords = torch.stack([torch.tensor(self.grid_X), torch.tensor(self.grid_Y)], dim=-1).float() |
| self.grid_coords_norm = self.grid_coords.clone() |
| self.grid_coords_norm[..., 0] = 2 * (self.grid_coords[..., 0] - x_min) / (x_max - x_min) - 1 |
| self.grid_coords_norm[..., 1] = 2 * (self.grid_coords[..., 1] - y_min) / (y_max - y_min) - 1 |
|
|
| if mean_cols >= 6: mean_vals = raw_mean[:, 3:6] |
| elif mean_cols == 5: mean_vals = raw_mean[:, 2:5] |
| else: mean_vals = raw_mean[:, -3:] |
|
|
| self.mean_data = griddata(coords_mean, mean_vals, (self.grid_X, self.grid_Y), method='linear', fill_value=0) |
| self.mean_data = torch.from_numpy(self.mean_data).permute(2, 0, 1).float() |
|
|
| unsteady_cols = raw_unsteady.shape[-1] if raw_unsteady.ndim > 1 else raw_mean.shape[-1] |
| points_per_frame = raw_mean.shape[0] |
| total_elements = raw_unsteady.size |
| frame_size = points_per_frame * unsteady_cols |
| num_frames = total_elements // frame_size |
| raw_unsteady = raw_unsteady.flatten()[:num_frames * frame_size] |
| raw_unsteady = raw_unsteady.reshape(num_frames, points_per_frame, unsteady_cols) |
|
|
| process_frames = min(num_frames, 200) |
| data_list = [] |
| for i in range(process_frames): |
| frame_data = raw_unsteady[i] |
| if unsteady_cols >= 6: values = frame_data[:, 3:6] |
| elif unsteady_cols == 5: values = frame_data[:, 2:5] |
| else: values = frame_data[:, :3] |
| coords = frame_data[:, :2] if unsteady_cols >= 5 else coords_mean |
| grid_val = griddata(coords, values, (self.grid_X, self.grid_Y), method='linear', fill_value=0) |
| data_list.append(grid_val) |
| |
| self.data = np.stack(data_list, axis=0) |
| self.data = torch.from_numpy(self.data).permute(0, 3, 1, 2).float() |
|
|
| self.stats = {} |
| self.max_vals = torch.amax(self.data, dim=(0, 2, 3), keepdim=True) |
| self.min_vals = torch.amin(self.data, dim=(0, 2, 3), keepdim=True) |
| self.stats['max'] = self.max_vals |
| self.stats['min'] = self.min_vals |
| self.stats['dt'] = self.dt |
| self.stats['box_len'] = self.box_len |
| |
| denom = self.max_vals - self.min_vals |
| denom[denom < 1e-8] = 1.0 |
| self.data = 2 * (self.data - self.min_vals) / denom - 1 |
| self.mean_data = 2 * (self.mean_data - self.min_vals.squeeze(0)) / denom.squeeze(0) - 1 |
| |
| print(f"[Dataset] Selecting {sensor_count} sensors (QR Pivot)...") |
| self.sensor_indices = self.compute_qr_sensors(self.data, self.mean_data, sensor_count) |
|
|
| def compute_qr_sensors(self, data, mean, num_sensors): |
| T, C, H, W = data.shape |
| fluctuations = (data[:, 0, :, :] - mean[0, :, :]).reshape(T, -1).numpy().T |
| k = min(T, num_sensors + 10) |
| U, _, _ = np.linalg.svd(fluctuations, full_matrices=False) |
| Psi = U[:, :k] |
| _, _, P = qr(Psi.T, pivoting=True) |
| best_indices_flat = P[:num_sensors] |
| sensor_locs = [] |
| for idx in best_indices_flat: |
| y = idx // W |
| x = idx % W |
| sensor_locs.append((y, x)) |
| return sensor_locs |
|
|
| def denormalize(self, tensor): |
| return (tensor + 1) / 2 * (self.stats['max'].to(tensor.device) - self.stats['min'].to(tensor.device)) + self.stats['min'].to(tensor.device) |
|
|
| def __len__(self): |
| return max(0, self.data.shape[0] - 1) |
|
|
| def __getitem__(self, idx): |
| if idx >= self.data.shape[0] - 1: idx = self.data.shape[0] - 2 |
| target_t = self.data[idx] |
| target_next = self.data[idx + 1] |
| |
| vals_t, coords_t = [], [] |
| for y, x in self.sensor_indices: |
| vals_t.append(target_t[:, y, x]) |
| coords_t.append(self.grid_coords_norm[y, x]) |
| |
| vals_next = [] |
| for y, x in self.sensor_indices: |
| vals_next.append(target_next[:, y, x]) |
| |
| return (torch.stack(vals_t), torch.stack(coords_t), self.grid_coords_norm, |
| torch.stack(vals_next), self.mean_data) |