| """Generate compact AVHRR-like daily SST data for engineering validation.""" |
|
|
| 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"] |
| n = int(data_config["samples"]) |
| height, width = map(int, data_config["grid"]) |
| if height != width or height < 16 or height % 16 or n < 4: |
| raise ValueError("DINCAE engineering grids must be square, divisible by 16, and contain at least four days") |
| rng = np.random.default_rng(int(config["seed"])) |
| yy, xx = np.mgrid[-1:1:complex(height), -1:1:complex(width)] |
| longitude = (12.0 + 6.0 * xx).astype(np.float32) |
| latitude = (38.0 + 6.0 * yy).astype(np.float32) |
| ocean = ((xx + 0.28) ** 2 + (yy - 0.10) ** 2 > 0.055) & (xx - 0.75 * yy < 1.12) |
| sst = np.empty((n, height, width), np.float32) |
| observed = np.empty_like(sst) |
| precision = np.empty_like(sst) |
| start = date.fromisoformat(data_config["start_date"]) |
| timestamps = [] |
| for t in range(n): |
| current = start + timedelta(days=t) |
| timestamps.append(current.isoformat()) |
| phase = 2 * np.pi * t / max(n, 16) |
| eddy = 1.9 * np.exp(-((xx - 0.35 * np.sin(phase)) ** 2 + |
| (yy - 0.25 * np.cos(phase)) ** 2) / 0.055) |
| field = 18.0 - 3.1 * yy + 0.7 * np.sin(3 * xx + phase) + eddy |
| field += rng.normal(0, 0.06, (height, width)) |
| field[~ocean] = np.nan |
| cloud_noise = rng.normal(size=(height, width)) |
| cloud = (np.sin(5 * xx + phase) + np.cos(4 * yy - phase) + cloud_noise > 1.05) |
| valid = ocean & ~cloud |
| sigma = (0.22 + 0.08 * (1 + np.sin(2 * xx - yy + phase))).astype(np.float32) |
| obs = field + rng.normal(0, sigma) |
| obs[~valid] = np.nan |
| sst[t], observed[t] = field, obs |
| precision[t] = np.where(valid, 1.0 / sigma ** 2, 0.0) |
| climatology = np.zeros((height, width), np.float32) |
| climatology[ocean] = np.mean(sst[:, ocean], axis=0) |
| anomaly = sst - climatology |
| observed_anomaly = observed - climatology |
| output = ROOT / data_config["root"] |
| output.mkdir(parents=True, exist_ok=True) |
| np.savez_compressed(output / data_config["file"], |
| format_version=np.array(data_config["format_version"]), |
| sst_anomaly=anomaly, observed_anomaly=observed_anomaly, |
| precision=precision, climatology=climatology, |
| ocean_mask=ocean, longitude=longitude, latitude=latitude, |
| timestamps=np.asarray(timestamps), units=np.array("degree_Celsius"), |
| grid=np.array([height, width], np.int32), |
| source=np.array("synthetic AVHRR-like engineering data")) |
| print(f"generated={n} grid={height}x{width} observed_fraction={np.isfinite(observed).mean():.4f}") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|