"""Generate compact structured daily precipitation data on the real target grid.""" from datetime import date, timedelta from pathlib import Path import numpy as np import yaml ROOT = Path(__file__).resolve().parents[1] def main(): config = yaml.safe_load((ROOT / "conf/config.yaml").read_text()) data_config = config["data"] samples = int(data_config["samples"]) height, width = map(int, data_config["target_grid"]) coarse_height, coarse_width = map(int, data_config["coarse_grid"]) if (height, width) != (216, 488) or samples < 3: raise ValueError("target grid must be 216x488 and at least three days are required") rng = np.random.default_rng(int(config["seed"])) y, x = np.mgrid[-1:1:complex(height), -1:1:complex(width)] elevation = (2600 * np.exp(-((x + 0.2) ** 2 / 0.12 + (y - 0.05) ** 2 / 0.35)) + 900 * np.exp(-((x - 0.55) ** 2 / 0.035 + (y + 0.35) ** 2 / 0.09))) elevation += 180 * (np.sin(8 * x) * np.cos(5 * y) + 1) elevation = np.maximum(elevation, 0).astype(np.float32) target = np.empty((samples, 1, height, width), dtype=np.float32) for day in range(samples): phase = 2 * np.pi * day / max(samples, 4) cx, cy = -0.75 + 1.5 * day / max(samples - 1, 1), 0.35 * np.sin(phase) moving_system = 18 * np.exp(-((x - cx) ** 2 / 0.10 + (y - cy) ** 2 / 0.16)) front = 7 * np.exp(-((y - 0.28 * np.sin(2 * x + phase)) ** 2) / 0.025) terrain_enhancement = 5.5 * (elevation / max(float(elevation.max()), 1.0)) ** 1.4 extreme = np.zeros_like(x) if day % 4 == 2: extreme = 42 * np.exp(-((x - 0.35) ** 2 + (y + 0.18) ** 2) / 0.008) noise = rng.gamma(1.2, 0.35, (height, width)) target[day, 0] = np.maximum(moving_system + front + terrain_enhancement + extreme + noise - 3.0, 0) # Area means produce a coarse observation; model code performs the external bilinear upsampling. row_edges = np.linspace(0, height, coarse_height + 1, dtype=int) col_edges = np.linspace(0, width, coarse_width + 1, dtype=int) coarse = np.empty((samples, 1, coarse_height, coarse_width), dtype=np.float32) for row in range(coarse_height): for column in range(coarse_width): block = target[:, :, row_edges[row]:row_edges[row + 1], col_edges[column]:col_edges[column + 1]] coarse[:, :, row, column] = block.mean(axis=(2, 3)) start = date.fromisoformat(data_config["start_date"]) timestamps = np.asarray([(start + timedelta(days=i)).isoformat() for i in range(samples)]) years = np.asarray([(start + timedelta(days=i)).year for i in range(samples)], dtype=np.int32) output = ROOT / data_config["root"] output.mkdir(parents=True, exist_ok=True) np.savez_compressed(output / "daily_precipitation.npz", format_version=np.array(data_config["format_version"]), coarse_precipitation=coarse, elevation=elevation[None, None], target_precipitation=target, timestamps=timestamps, years=years, units=np.array("mm/day"), target_grid=np.array([height, width], np.int32)) print(f"generated={samples} target_shape={target.shape} coarse_shape={coarse.shape}") if __name__ == "__main__": main()