PINN-TC / scripts /inference.py
zhangrenchao's picture
Upload folder using huggingface_hub
2f3c9e4 verified
Raw
History Blame Contribute Delete
3.83 kB
"""Run chunked inference on the full 251 x 251 x 44 x 3 paper-resolution domain."""
import importlib.util
from pathlib import Path
import numpy as np
import torch
import yaml
ROOT = Path(__file__).resolve().parents[1]
def load_model_module():
spec = importlib.util.spec_from_file_location("pinn_tc_model", ROOT / "model/pinn-tc.py")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def coordinates_for_indices(indices, y_axis, x_axis, times, pressures):
x_count, p_count, t_count = len(x_axis), len(pressures), len(times)
y_index = indices // (x_count * p_count * t_count)
remainder = indices % (x_count * p_count * t_count)
x_index = remainder // (p_count * t_count)
remainder %= p_count * t_count
p_index = remainder // t_count
t_index = remainder % t_count
return np.column_stack((y_axis[y_index], x_axis[x_index], times[t_index], pressures[p_index])).astype(np.float32)
def main():
config = yaml.safe_load((ROOT / "conf/config.yaml").read_text())
module = load_model_module()
requested = config["runtime"]["device"]
device = torch.device("cuda" if requested == "auto" and torch.cuda.is_available() else ("cpu" if requested == "auto" else requested))
checkpoint = torch.load(ROOT / config["paths"]["checkpoint"], map_location=device, weights_only=False)
if checkpoint.get("format_version") != config["data"]["format_version"]:
raise ValueError("checkpoint format_version mismatch")
if checkpoint["input_order"] != list(module.INPUT_ORDER) or checkpoint["output_order"] != list(module.OUTPUT_ORDER):
raise ValueError("checkpoint coordinate or variable protocol mismatch")
model = module.PINNTC(**checkpoint["model_config"]).to(device)
model.load_state_dict(checkpoint["model_state_dict"]); model.eval()
data = config["data"]
y_axis = np.linspace(-data["horizontal_extent_m"], data["horizontal_extent_m"], data["grid_points"], dtype=np.float32)
x_axis = y_axis.copy()
times = np.asarray(data["observation_times_s"], dtype=np.float32)
pressures = np.linspace(data["pressure_min_pa"], data["pressure_max_pa"], data["pressure_levels"], dtype=np.float32)
shape = (len(y_axis), len(x_axis), len(pressures), len(times), 4)
expected = (251, 251, 44, 3, 4)
if shape != expected:
raise ValueError(f"dense grid must remain {expected}, got {shape}")
output = ROOT / config["paths"]["prediction"]
output.parent.mkdir(parents=True, exist_ok=True)
temporary = output.with_suffix(".work.npy")
prediction = np.lib.format.open_memmap(temporary, mode="w+", dtype=np.float32, shape=shape)
flat = prediction.reshape(-1, 4)
chunk_size = int(config["runtime"]["inference_chunk_size"])
with torch.inference_mode():
for start in range(0, len(flat), chunk_size):
stop = min(start + chunk_size, len(flat))
coordinates = coordinates_for_indices(np.arange(start, stop, dtype=np.int64), y_axis, x_axis, times, pressures)
values = model(torch.from_numpy(coordinates).to(device)).cpu().numpy().astype(np.float32)
if not np.isfinite(values).all():
raise FloatingPointError(f"non-finite prediction in rows {start}:{stop}")
flat[start:stop] = values
prediction.flush()
np.savez(output, predictions=prediction, y=y_axis, x=x_axis, time=times, pressure=pressures,
input_order=np.asarray(module.INPUT_ORDER), output_order=np.asarray(module.OUTPUT_ORDER),
format_version=np.asarray(config["data"]["format_version"]), layout=np.asarray("YXPTV"))
del prediction
temporary.unlink()
print(f"predictions={output.relative_to(ROOT)} shape={shape} bytes={output.stat().st_size}")
if __name__ == "__main__":
main()