UnetDif / scripts /fake_data.py
zhangrenchao's picture
Upload folder using huggingface_hub
30f7852 verified
Raw
History Blame Contribute Delete
3.96 kB
"""Generate grouped synthetic rain days at the real 39x56x56 input shape."""
from pathlib import Path
import numpy as np
import yaml
ROOT = Path(__file__).resolve().parents[1]
def smooth(field: np.ndarray, rounds: int = 2) -> np.ndarray:
for _ in range(rounds):
field = sum(np.roll(np.roll(field, y, -2), x, -1) for y, x in ((0, 0), (1, 0), (-1, 0), (0, 1), (0, -1))) / 5
return field
def make_day(rng: np.random.Generator, steps: int, height: int, width: int):
yy, xx = np.mgrid[-1:1:complex(height), -1:1:complex(width)].astype(np.float32)
terrain = np.clip(0.55 + 0.25 * yy + 0.16 * np.sin(2.5 * xx) - 0.12 * np.cos(3 * yy), 0, 1)
center = rng.uniform(-0.45, 0.45, 2)
velocity = rng.uniform(-0.10, 0.10, 2)
width0 = rng.uniform(0.13, 0.27)
amplitude = rng.uniform(12, 28)
inputs = np.empty((steps, 39, height, width), np.float32)
targets = np.empty((steps, height, width), np.float32)
for step in range(steps):
cy, cx = center + velocity * step
rain = amplitude * np.exp(-((xx - cx) ** 2 + (yy - cy) ** 2) / (2 * width0**2))
rain += 0.35 * amplitude * np.exp(-((xx + cx * 0.5) ** 2 + (yy - cy * 0.6) ** 2) / (3 * width0**2))
rain = np.clip(rain * (0.8 + 0.3 * terrain) + rng.normal(0, 0.12, rain.shape), 0, None)
forecast = np.clip(np.roll(rain, shift=(1, -1), axis=(0, 1)) * (1.12 + 0.08 * terrain) - 0.25, 0, None)
channels = np.empty((39, height, width), np.float32)
for variable in range(8):
for level in range(4):
index = variable * 4 + level
noise = smooth(rng.normal(0, 0.05, rain.shape).astype(np.float32))
if variable == 1: # humidity follows rain and terrain
channels[index] = 0.45 + 0.02 * rain + 0.12 * terrain - 0.025 * level + noise
elif variable == 4: # vertical velocity is strongest near moving rain
channels[index] = -0.08 * rain / (level + 1) + 0.04 * (xx * velocity[1] + yy * velocity[0]) + noise
else:
channels[index] = (variable + 1) * 0.12 + level * 0.04 + 0.08 * xx - 0.05 * yy + noise
for index in range(32, 37):
channels[index] = 0.1 * (index - 31) + 0.03 * rain + smooth(rng.normal(0, 0.04, rain.shape).astype(np.float32))
channels[37] = forecast
channels[38] = terrain
inputs[step], targets[step] = channels, rain
return inputs, targets, terrain
def main():
config = yaml.safe_load((ROOT / "conf/config.yaml").read_text())
data = config["data"]
expected = (8, 39, 56, 56)
actual = (int(data["time_steps"]), int(data["channels"]), int(data["height"]), int(data["width"]))
if actual != expected or len(data["lead_hours"]) != expected[0]:
raise ValueError(f"fixed data contract is {expected} with eight lead hours, got {actual}")
counts = data["rain_days"]
output = ROOT / data["root"]
output.mkdir(parents=True, exist_ok=True)
seed = int(config["seed"])
group_id = 0
for split_index, split in enumerate(("train", "val", "test")):
arrays, targets, terrains, groups = [], [], [], []
for day in range(int(counts[split])):
x, y, terrain = make_day(np.random.default_rng(seed + group_id), int(data["time_steps"]), int(data["height"]), int(data["width"]))
arrays.append(x); targets.append(y); terrains.append(terrain); groups.append(group_id)
group_id += 1
np.savez_compressed(
output / f"{split}.npz", inputs=np.stack(arrays), targets=np.stack(targets),
terrain=np.stack(terrains), group_id=np.asarray(groups), split=np.asarray(split),
format_version=np.asarray(data["format_version"]), lead_hours=np.asarray(data["lead_hours"]),
)
print(f"split={split} rain_days={counts[split]} inputs={np.stack(arrays).shape}")
if __name__ == "__main__":
main()