Source code for pypielm.data.adapters.pdebench_adapter
"""PDEBench dataset adapter.
PDEBench (https://github.com/pdebench/PDEBench) distributes datasets as HDF5
files. This adapter reads a single PDE task file and converts it to
:class:`~pypielm.data.dataset.PIELMDataset`.
h5py is an optional dependency — a clear ``ImportError`` is raised if missing.
"""
from __future__ import annotations
from pathlib import Path
import torch
from pypielm.data.dataset import PIELMDataset
[docs]
class PDEBenchAdapter:
"""Load a :class:`~pypielm.data.dataset.PIELMDataset` from a PDEBench HDF5 file.
The adapter reads the spatial grid coordinates (``x``) and the solution
field (``u``) and flattens them into ``(N, d)`` and ``(N, m)`` arrays
suitable for a PIELM collocation problem.
Args:
path: Path to the ``.h5`` / ``.hdf5`` file.
equation: Equation name key inside the HDF5 file (top-level group),
e.g. ``'1D_Advection'``. When ``None`` the first top-level group
is used.
sample_idx: Index of the trajectory/sample to load (time snapshot 0
is used as the target field).
dtype: Tensor dtype.
device: Target device.
"""
def __init__(
self,
path: str | Path,
equation: str | None = None,
sample_idx: int = 0,
dtype: torch.dtype = torch.float64,
device: str | torch.device = "cpu",
) -> None:
self.path = Path(path)
self.equation = equation
self.sample_idx = sample_idx
self.dtype = dtype
self.device = device
[docs]
def load(self) -> PIELMDataset: # pragma: no cover
"""Load and return the dataset.
Requires the optional ``h5py`` dependency and an actual HDF5 file;
excluded from automated coverage runs.
"""
try:
import h5py # type: ignore[import-untyped]
except ImportError as exc:
raise ImportError(
"h5py is required for PDEBenchAdapter. Install it with:\n"
" pip install h5py"
) from exc
import numpy as np
with h5py.File(self.path, "r") as f:
eq_key = self.equation
if eq_key is None:
eq_key = list(f.keys())[0]
if eq_key not in f:
raise KeyError(
f"Equation key '{eq_key}' not found in '{self.path}'. "
f"Available keys: {list(f.keys())}"
)
grp = f[eq_key]
# Try common PDEBench layout: grp['x'] for coordinates, grp['u'] for solution
# Fallback: treat all numeric datasets as candidate arrays
x_arr: np.ndarray | None = None
u_arr: np.ndarray | None = None
for cand in ("x", "coords", "grid"):
if cand in grp:
x_arr = np.asarray(grp[cand])
break
for cand in ("u", "sol", "solution", "output"):
if cand in grp:
u_arr = np.asarray(grp[cand])
break
# If u is 3-D (sample, time, space) take the requested sample at t=0
if u_arr is not None and u_arr.ndim == 3:
u_arr = u_arr[self.sample_idx, 0, :] # (space,)
elif u_arr is not None and u_arr.ndim == 2:
u_arr = u_arr[self.sample_idx, :]
if x_arr is None or u_arr is None:
raise ValueError(
f"Could not locate coordinate/solution arrays in group '{eq_key}'. "
"Provide explicit dataset keys via the 'equation' argument or "
"extend this adapter for your file layout."
)
# Flatten to 2-D
if x_arr.ndim == 1:
x_arr = x_arr[:, None]
if u_arr.ndim == 1:
u_arr = u_arr[:, None]
def _t(a: np.ndarray) -> torch.Tensor:
return torch.tensor(a, dtype=self.dtype, device=self.device)
return PIELMDataset(
X_colloc=_t(x_arr),
y_data=_t(u_arr),
meta={"source": "pdebench", "equation": eq_key, "file": str(self.path)},
)