{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":39763,"databundleVersionId":11756775,"sourceType":"competition"},{"sourceId":12000386,"sourceType":"datasetVersion","datasetId":7548872}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"**IMPORTING LIBRARY**","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport os\nfrom glob import glob\nimport matplotlib.pyplot as plt\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom sklearn.preprocessing import StandardScaler, MinMaxScaler\nfrom sklearn.model_selection import train_test_split","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-05-30T17:14:11.265389Z","iopub.execute_input":"2025-05-30T17:14:11.265629Z","iopub.status.idle":"2025-05-30T17:14:17.504163Z","shell.execute_reply.started":"2025-05-30T17:14:11.265607Z","shell.execute_reply":"2025-05-30T17:14:17.503042Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"base_dir = '/kaggle/input/waveform-inversion/train_samples'\n\nfolders = ['CurveVel_A', 'CurveVel_B', 'FlatVel_A', 'FlatVel_B', 'Style_A', 'Style_B']\n\nseismic_list = []\nvelocity_list = []\n\nfor folder in folders:\n    data_path = os.path.join(base_dir, folder, 'data')\n    model_path = os.path.join(base_dir, folder, 'model')\n\n    data_files = sorted(glob(os.path.join(data_path, '*.npy')))\n    model_files = sorted(glob(os.path.join(model_path, '*.npy')))\n\n    if len(data_files) != len(model_files):\n        raise ValueError(\"Mismatch in \" + folder)\n\n    for data_file, model_file in zip(data_files, model_files):\n        seismic = np.load(data_file)\n        velocity = np.load(model_file)\n\n        seismic_list.append(seismic)\n        velocity_list.append(velocity)\n\nprint(\"Total samples loaded: {}\".format(len(seismic_list)))\nprint(\"Seismic shape: {}\".format(seismic_list[0].shape))\nprint(\"Velocity map shape: {}\".format(velocity_list[0].shape))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T17:14:17.506172Z","iopub.execute_input":"2025-05-30T17:14:17.506830Z","iopub.status.idle":"2025-05-30T17:15:12.536973Z","shell.execute_reply.started":"2025-05-30T17:14:17.506801Z","shell.execute_reply":"2025-05-30T17:15:12.534778Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for folder in folders:\n    print(\"Checking folder:\", folder)\n\n    data_path = os.path.join(base_dir, folder, 'data')\n    model_path = os.path.join(base_dir, folder, 'model')\n\n    data_files = sorted(glob(os.path.join(data_path, '*.npy')))\n    model_files = sorted(glob(os.path.join(model_path, '*.npy')))\n\n    if len(data_files) == 0 or len(model_files) == 0:\n        print(\"No .npy files found in either 'data' or 'model'.\")\n        continue\n\n    velocity = np.load(model_files[0])\n\n    if len(velocity.shape) == 2:\n        rows, cols = velocity.shape\n        print(\"Velocity map shape: rows =\", rows, \", columns =\", cols)\n    else:\n        print(\"Velocity map is not 2D, shape:\", velocity.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T17:15:12.539763Z","iopub.execute_input":"2025-05-30T17:15:12.540332Z","iopub.status.idle":"2025-05-30T17:15:12.685209Z","shell.execute_reply.started":"2025-05-30T17:15:12.540257Z","shell.execute_reply":"2025-05-30T17:15:12.683350Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"velocity = np.load('/kaggle/input/waveform-inversion/train_samples/Style_B/model/model1.npy')\n\nsingle_map = velocity[0, 0]\n\nplt.imshow(single_map, cmap='viridis')\nplt.title(\"Velocity Map Sample (Style A model1)\")\nplt.colorbar(label='Velocity')\nplt.xlabel(\"X\")\nplt.ylabel(\"Y\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T17:15:12.686793Z","iopub.execute_input":"2025-05-30T17:15:12.687179Z","iopub.status.idle":"2025-05-30T17:15:13.236791Z","shell.execute_reply.started":"2025-05-30T17:15:12.687141Z","shell.execute_reply":"2025-05-30T17:15:13.235630Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"velocity_model2 = np.load('/kaggle/input/waveform-inversion/train_samples/Style_A/model/model2.npy')\n\nprint(\"Minimum velocity model2:\", velocity_model2.min())\nprint(\"Maximum velocity model2:\", velocity_model2.max())\n\nsingle_map_model2 = velocity_model2[0, 0]\n\nplt.imshow(single_map_model2, cmap='viridis')\nplt.title(\"Velocity Map Sample (Style A model2)\")\nplt.colorbar(label='Velocity in m/s (meter per second)')\nplt.xlabel(\"X\")\nplt.ylabel(\"Y\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T17:15:13.238076Z","iopub.execute_input":"2025-05-30T17:15:13.238484Z","iopub.status.idle":"2025-05-30T17:15:13.647592Z","shell.execute_reply.started":"2025-05-30T17:15:13.238450Z","shell.execute_reply":"2025-05-30T17:15:13.646316Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**CURVE FAULT A SEISMIC**","metadata":{}},{"cell_type":"code","source":"seismic = np.load('/kaggle/input/waveform-inversion/train_samples/CurveFault_A/seis2_1_0.npy')\n\nprint(\"Seismic shape:\", seismic.shape)\n\ntrace = seismic[0, 2, :, 10]\n\nplt.plot(trace)\nplt.title(\"Seismic Trace (Sample 0, Source 2, Receiver 10)\")\nplt.xlabel(\"Time Step\")\nplt.ylabel(\"Amplitude\")\nplt.grid(True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T17:15:13.649561Z","iopub.execute_input":"2025-05-30T17:15:13.649980Z","iopub.status.idle":"2025-05-30T17:15:18.983747Z","shell.execute_reply.started":"2025-05-30T17:15:13.649949Z","shell.execute_reply":"2025-05-30T17:15:18.982542Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample = 0\nsource = 2\n\nplt.figure(figsize=(10, 6))\noffset = 5\n\nfor i in range(5):\n    trace = seismic[sample, source, :, i]\n    plt.plot(trace + i * offset, label='Receiver ' + str(i), alpha=0.8)\n\nplt.title(\"Offset Seismic Traces (Sample 0, Source 2, Receivers 0–4)\")\nplt.xlabel(\"Time Step\")\nplt.ylabel(\"Amplitude + Offset\")\nplt.legend()\nplt.grid(True)\nplt.tight_layout()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T17:15:18.986465Z","iopub.execute_input":"2025-05-30T17:15:18.986756Z","iopub.status.idle":"2025-05-30T17:15:19.440589Z","shell.execute_reply.started":"2025-05-30T17:15:18.986733Z","shell.execute_reply":"2025-05-30T17:15:19.439440Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**BUILD THE DATA PIPELINE AND PREPARE SEISMIC FEATURES FOR MODELING**\n","metadata":{}},{"cell_type":"code","source":"flat_seismic_list = []\nflat_velocity_list = []\n\nfor seismic_batch, velocity_batch in zip(seismic_list, velocity_list):\n    for i in range(seismic_batch.shape[0]):\n        flat_seismic_list.append(seismic_batch[i])\n        flat_velocity_list.append(velocity_batch[i])\n\nclass WaveformDataset(Dataset):\n    def __init__(self, seismic_list, velocity_list):\n        self.seismic_list = seismic_list\n        self.velocity_list = velocity_list\n\n    def __len__(self):\n        return len(self.seismic_list)\n\n    def __getitem__(self, idx):\n        seismic = self.seismic_list[idx].astype(np.float32)\n        velocity = self.velocity_list[idx].astype(np.float32)\n\n        mean = seismic.mean()\n        std = seismic.std()\n        if std > 0:\n            seismic = (seismic - mean) / std\n\n        v_min = velocity.min()\n        v_max = velocity.max()\n        if v_max > v_min:\n            velocity = (velocity - v_min) / (v_max - v_min)\n\n        seismic = torch.tensor(seismic, dtype=torch.float32)\n        velocity = torch.tensor(velocity, dtype=torch.float32)\n\n        if velocity.ndim == 3 and velocity.shape[0] == 1:\n            velocity = velocity.squeeze(0)\n\n        return seismic, velocity\n\ndataset = WaveformDataset(flat_seismic_list, flat_velocity_list)\ndataloader = DataLoader(dataset, batch_size=8, shuffle=True, num_workers=0)\n\nclass UNet(nn.Module):\n    def __init__(self, in_channels=5, out_channels=1):\n        super(UNet, self).__init__()\n\n        def conv_block(in_c, out_c):\n            \n            return nn.Sequential(\n                nn.Conv2d(in_c, out_c, kernel_size=3, padding=1),\n                nn.BatchNorm2d(out_c),\n                nn.ReLU(inplace=True),\n                nn.Conv2d(out_c, out_c, kernel_size=3, padding=1),\n                nn.BatchNorm2d(out_c),\n                nn.ReLU(inplace=True)\n            )\n\n        self.enc1 = conv_block(in_channels, 64)\n        self.pool1 = nn.MaxPool2d(2)\n        self.enc2 = conv_block(64, 128)\n        self.pool2 = nn.MaxPool2d(2)\n        self.enc3 = conv_block(128, 256)\n\n        self.up1 = nn.ConvTranspose2d(256, 128, kernel_size=2, stride=2, output_padding=1)\n        self.dec1 = conv_block(256, 128)\n        self.up2 = nn.ConvTranspose2d(128, 64, kernel_size=2, stride=2)\n        self.dec2 = conv_block(128, 64)\n\n        self.out = nn.Conv2d(64, out_channels, kernel_size=1)\n\n    def forward(self, x):\n        e1 = self.enc1(x)\n        p1 = self.pool1(e1)\n        e2 = self.enc2(p1)\n        p2 = self.pool2(e2)\n        e3 = self.enc3(p2)\n\n        d1 = self.up1(e3)\n        d1 = torch.cat([d1, e2], dim=1) \n        d1 = self.dec1(d1)\n\n        d2 = self.up2(d1)\n        d2 = torch.cat([d2, e1], dim=1)\n        d2 = self.dec2(d2)\n\n        return self.out(d2)\nmodel = UNet(in_channels=5, out_channels=1)\n\nif torch.cuda.is_available():\n    device = 'cuda'\nelse:\n    device = 'cpu'\n\nmodel = model.to(device)\n\nfor seismic_batch, velocity_batch in dataloader:\n    seismic_batch = seismic_batch.to(device)\n    velocity_batch = velocity_batch.to(device)\n\n    print(\"Input shape before forward:\", seismic_batch.shape)\n    print(\"Velocity shape:\", velocity_batch.shape)\n\n    break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T17:15:19.441989Z","iopub.execute_input":"2025-05-30T17:15:19.442371Z","iopub.status.idle":"2025-05-30T17:15:19.718926Z","shell.execute_reply.started":"2025-05-30T17:15:19.442336Z","shell.execute_reply":"2025-05-30T17:15:19.717219Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.eval()\nwith torch.no_grad():\n    for seismic_batch_eval, velocity_batch_eval in dataloader:\n        seismic_batch_eval = seismic_batch_eval.to(device)\n        velocity_batch_eval = velocity_batch_eval.to(device)\n\n        if seismic_batch_eval.ndim == 4:\n            input_for_model_eval = seismic_batch_eval.permute(0, 1, 3, 2)\n        else:\n            input_for_model_eval = seismic_batch_eval\n        input_for_model_eval = input_for_model_eval[:, :, :70, :70]\n\n        output = model(input_for_model_eval)\n        \n        pred = output[0].squeeze().cpu().numpy()\n        target = velocity_batch[0].squeeze().cpu().numpy()\n\n        plt.figure(figsize=(10, 4))\n\n        plt.subplot(1, 2, 1)\n        plt.imshow(target, cmap='viridis')\n        plt.title(\"Ground Truth Velocity\")\n        plt.colorbar()\n\n        plt.subplot(1, 2, 2)\n        plt.imshow(pred, cmap='viridis')\n        plt.title(\"Predicted Velocity\")\n        plt.colorbar()\n\n        plt.suptitle(\"Model Prediction vs Ground Truth\")\n        plt.tight_layout()\n\n        break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T17:15:19.719628Z","iopub.execute_input":"2025-05-30T17:15:19.719878Z","iopub.status.idle":"2025-05-30T17:15:21.080297Z","shell.execute_reply.started":"2025-05-30T17:15:19.719857Z","shell.execute_reply":"2025-05-30T17:15:21.079336Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**TRAINING THE MODEL**","metadata":{}},{"cell_type":"code","source":"num_epochs = 10\n\ncriterion = nn.L1Loss()\noptimizer = optim.Adam(model.parameters(), lr=1e-4)\n\nfor epoch in range(num_epochs):\n    model.train()\n    running_loss = 0.0\n\n    for seismic_batch, velocity_batch in dataloader:\n        seismic_batch = seismic_batch.to(device)\n        velocity_batch = velocity_batch.to(device)\n\n        optimizer.zero_grad()\n\n        if seismic_batch.ndim == 4:\n            seismic_batch = seismic_batch.permute(0, 1, 3, 2)\n\n   \n        seismic_batch = seismic_batch[:, :, :70, :70]\n\n        if velocity_batch.ndim == 3:\n            velocity_batch = velocity_batch.unsqueeze(1)\n\n        output = model(seismic_batch)\n\n       \n        velocity_batch = velocity_batch[:, :, :70, :70]\n\n        loss = criterion(output, velocity_batch)\n\n        loss.backward()\n        optimizer.step()\n        running_loss += loss.item()\n\n    avg_loss = running_loss / len(dataloader)\n    print(\"Epoch {}/{} , Average Loss: {:.4f}\".format(epoch + 1, num_epochs, avg_loss))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T17:15:21.081306Z","iopub.execute_input":"2025-05-30T17:15:21.082008Z","iopub.status.idle":"2025-05-30T19:28:04.107810Z","shell.execute_reply.started":"2025-05-30T17:15:21.081971Z","shell.execute_reply":"2025-05-30T19:28:04.104017Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**FINDING MAE (MEAN ABSOLUTE ERROR)**","metadata":{}},{"cell_type":"code","source":"from sklearn.metrics import mean_absolute_error\n\nmodel.eval()\nval_mae = []\n\nwith torch.no_grad():\n    for seismic_batch, velocity_batch in dataloader:  \n        seismic_batch = seismic_batch.to(device)\n        velocity_batch = velocity_batch.to(device)\n\n        if seismic_batch.ndim == 4:\n            seismic_batch = seismic_batch.permute(0, 1, 3, 2)\n\n        seismic_batch = seismic_batch[:, :, :70, :70]\n        velocity_batch = velocity_batch.unsqueeze(1) if velocity_batch.ndim == 3 else velocity_batch\n        velocity_batch = velocity_batch[:, :, :70, :70]\n\n        preds = model(seismic_batch)\n\n        preds_flat = preds.view(preds.size(0), -1).cpu().numpy()\n        targets_flat = velocity_batch.view(velocity_batch.size(0), -1).cpu().numpy()\n\n        for pred, target in zip(preds_flat, targets_flat):\n            mae = mean_absolute_error(target, pred)\n            val_mae.append(mae)\n\nprint(\"Validation MAE: {:.4f}\".format(np.mean(val_mae)))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T19:28:04.113656Z","iopub.execute_input":"2025-05-30T19:28:04.117186Z","iopub.status.idle":"2025-05-30T19:32:17.720843Z","shell.execute_reply.started":"2025-05-30T19:28:04.117129Z","shell.execute_reply":"2025-05-30T19:32:17.719374Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**VISUALIZE PREDICTION VS ACTUAL VELOCITY**","metadata":{}},{"cell_type":"code","source":"model.eval()\n\nwith torch.no_grad():\n    for seismic_batch, velocity_batch in dataloader:\n        seismic_batch = seismic_batch.to(device)\n        velocity_batch = velocity_batch.to(device)\n\n        if seismic_batch.ndim == 4:\n            seismic_batch = seismic_batch.permute(0, 1, 3, 2)\n\n        seismic_batch = seismic_batch[:, :, :70, :70]\n        velocity_batch = velocity_batch.unsqueeze(1) if velocity_batch.ndim == 3 else velocity_batch\n        velocity_batch = velocity_batch[:, :, :70, :70]\n\n        preds = model(seismic_batch)\n\n        pred_sample = preds[0].squeeze().cpu().numpy()\n        target_sample = velocity_batch[0].squeeze().cpu().numpy()\n\n        plt.figure(figsize=(12, 5))\n\n        plt.subplot(1, 2, 1)\n        plt.imshow(target_sample, cmap='viridis')\n        plt.title(\"Ground Truth Velocity\")\n        plt.colorbar()\n\n        plt.subplot(1, 2, 2)\n        plt.imshow(pred_sample, cmap='viridis')\n        plt.title(\"Predicted Velocity\")\n        plt.colorbar()\n\n        plt.suptitle(\"Prediction vs. Ground Truth\")\n        plt.tight_layout()\n\n        break","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T19:32:17.722824Z","iopub.execute_input":"2025-05-30T19:32:17.723321Z","iopub.status.idle":"2025-05-30T19:32:18.883555Z","shell.execute_reply.started":"2025-05-30T19:32:17.723242Z","shell.execute_reply":"2025-05-30T19:32:18.882366Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**CREATING SUBMISSION CSV**","metadata":{}},{"cell_type":"code","source":"import csv\n\ntest_dir = '/kaggle/input/waveform-inversion/train_samples'\n\ntest_subfolders = [\n    'CurveFault_A', 'CurveFault_B', 'CurveVel_A', 'CurveVel_B',\n    'FlatFault_A', 'FlatFault_B', 'FlatVel_A', 'FlatVel_B',\n    'Style_A', 'Style_B'\n]\n\nall_test_npy_file_paths = []\nall_test_oids = []\n\nprint(f\"\\n--- Starting Inference: Iterating through subfolders in: {test_dir} ---\")\n\nfor subfolder_name in test_subfolders:\n    current_subfolder_path = os.path.join(test_dir, subfolder_name)\n\n    path_to_scan = os.path.join(current_subfolder_path, 'data')\n\n    if not os.path.isdir(path_to_scan):\n        print(f\"Warning: Path to scan '{path_to_scan}' not found or is not a directory for subfolder '{subfolder_name}'. Skipping.\")\n        continue\n    print(f\"  Scanning for .npy files in: {path_to_scan}\")\n    found_in_subfolder_count = 0  \n    try:\n        for f_path in glob(os.path.join(path_to_scan, '*.npy')):\n            all_test_npy_file_paths.append(f_path)\n            oid = os.path.basename(f_path).replace('.npy', '')\n            all_test_oids.append(oid)\n            found_in_subfolder_count += 1\n        if found_in_subfolder_count > 0:\n            print(f\"    Found {found_in_subfolder_count} .npy files in {path_to_scan}.\")\n        else:\n            print(f\"    No .npy files found in {path_to_scan}\")\n    except Exception as e:\n        print(f\"Error accessing or listing files in {path_to_scan}: {e}\")\n\nif not all_test_npy_file_paths: \n    print(\"CRITICAL ERROR: No .npy test files found in any of the specified subfolders after scanning. Cannot perform inference.\")\n    with open('submission.csv', mode='w', newline='') as f_csv:\n        writer = csv.writer(f_csv)\n        writer.writerow(['id'] + [f\"x_{i}\" for i in range(1, 70, 2)])\n    exit()\n\nprint(f\"Total .npy files found across all subfolders: {len(all_test_npy_file_paths)}\")\n\ntest_seismic_list = []\n\nfinal_test_oids_for_dataset = []\n\nfor i, file_path_to_load in enumerate(all_test_npy_file_paths): \n    try:\n        test_seismic_list.append(np.load(file_path_to_load))\n        final_test_oids_for_dataset.append(all_test_oids[i]) \n    except Exception as e:\n        print(f\"Warning: Could not load test file {file_path_to_load}: {e}. Skipping this file.\")\n\n\nif not test_seismic_list:\n    print(\"ERROR: No test data successfully loaded after attempting all found files. Cannot create submission.\")\n    exit()\n\nprint(f\"Successfully loaded {len(test_seismic_list)} seismic samples for testing.\")\n\nclass TestDataset(Dataset):\n    def __init__(self, seismic_list, oid_list):\n        self.seismic_list = seismic_list\n        self.oid_list = oid_list\n        print(f\"TestDataset initialized with {len(self.seismic_list)} samples.\")\n\n    def __len__(self):\n        return len(self.seismic_list)\n\n    def __getitem__(self, idx):\n        seismic = self.seismic_list[idx].astype(np.float32)\n        oid = self.oid_list[idx]\n\n        loaded_seismic_data_for_oid = self.seismic_list[idx]\n        oid = self.oid_list[idx]\n        single_seismic_sample = None\n\n        if loaded_seismic_data_for_oid.ndim == 4:\n            if loaded_seismic_data_for_oid.shape[0] > 1 and loaded_seismic_data_for_oid.shape[1] == 5:\n                print(f\"    Note: Test file for OID {oid} contains {loaded_seismic_data_for_oid.shape[0]} samples. Processing the first one.\")\n                single_seismic_sample = loaded_seismic_data_for_oid[0]\n\n            elif loaded_seismic_data_for_oid.shape[0] == 1 and loaded_seismic_data_for_oid.shape[\n                1] == 5:  \n                single_seismic_sample = loaded_seismic_data_for_oid.squeeze(\n                    0)  \n            else:\n                raise ValueError(\n                    f\"Unexpected 4D seismic data shape for OID {oid}: {loaded_seismic_data_for_oid.shape}. Expected (N, 5, 1500, 70) or (1, 5, 1500, 70).\")\n\n        elif loaded_seismic_data_for_oid.ndim == 3 and loaded_seismic_data_for_oid.shape[0] == 5:  \n            single_seismic_sample = loaded_seismic_data_for_oid\n        else:\n            raise ValueError(\n                f\"Unexpected seismic data shape for OID {oid}: {loaded_seismic_data_for_oid.shape}. Expected 3D (5, 1500, 70) or 4D.\")\n\n        seismic = single_seismic_sample.astype(np.float32)\n\n        mean = seismic.mean()\n        std = seismic.std()\n        if std > 0:\n            seismic = (seismic - mean) / std\n        else:\n            seismic = seismic - mean \n\n        seismic_tensor = torch.tensor(seismic, dtype=torch.float32)\n        return seismic_tensor, oid\n\ntest_dataset = TestDataset(test_seismic_list, final_test_oids_for_dataset)\ntest_dataloader = DataLoader(test_dataset, batch_size=1, shuffle=False) \nprint(f\"Test Dataloader created with {len(test_dataloader)} batches.\")\n\nmodel.eval()\n\nsubmission_file_path = 'submission.csv' \nprint(f\"Writing submission to: {submission_file_path}\")\nrows_written_count = 0\n\nwith open(submission_file_path, mode='w', newline='') as f_csv: \n    writer = csv.writer(f_csv)\n    header = ['id'] + [\"x_{}\".format(i) for i in range(1, 70, 2)] \n    writer.writerow(header)\n    print(\"CSV Header written.\")\n\n    if len(test_dataloader) == 0:\n        print(\"Test Dataloader is empty. No predictions to write.\")\n    else:\n        with torch.no_grad(): \n            for i, data_batch in enumerate(test_dataloader):\n                seismic_batch_test, oid_batch_test_tuple = data_batch  \n                current_oid = oid_batch_test_tuple[0] \n\n                print(f\"  Processing OID: {current_oid} (Batch {i + 1}/{len(test_dataloader)})\")\n                seismic_batch_test = seismic_batch_test.to(device) \n\n                if seismic_batch_test.ndim == 4:\n \n                    input_to_model_test = seismic_batch_test.permute(0, 1, 3, 2)\n                else:\n                    input_to_model_test = seismic_batch_test  \n\n                input_to_model_test = input_to_model_test[:, :, :70, :70]\n\n                output_pred = model(input_to_model_test) \n\n \n                pred_map_normalized = output_pred[0].squeeze(0).cpu().numpy()\n\n                final_pred_map = pred_map_normalized\n\n                odd_columns_submission = final_pred_map[:, 1::2] \n\n                for y_idx, row_values in enumerate(odd_columns_submission):\n                    row_label = \"{}_y_{}\".format(current_oid, y_idx)\n                    writer.writerow([row_label] + list(row_values))\n                    rows_written_count += 1\n    print(f\"Finished. Total data rows written to CSV: {rows_written_count}\")\n    if rows_written_count == 0 and len(test_dataloader) > 0:\n        print(\"WARNING: Test Dataloader was not empty, but no rows were written. Check loops or OID processing.\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-30T19:32:18.885318Z","iopub.execute_input":"2025-05-30T19:32:18.885691Z","iopub.status.idle":"2025-05-30T19:33:36.233845Z","shell.execute_reply.started":"2025-05-30T19:32:18.885661Z","shell.execute_reply":"2025-05-30T19:33:36.232515Z"}},"outputs":[],"execution_count":null}]}