PBL-Emulator / scripts /fake_data.py
zhangrenchao's picture
Publish PBL-Emulator engineering reproduction
702a3cf verified
Raw
History Blame Contribute Delete
3.62 kB
from pathlib import Path
import sys
import numpy as np
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from model.pbl_emulator import INPUT_NAMES, OUTPUT_NAMES, load_yaml
def main() -> None:
config = load_yaml(ROOT / "conf/config.yaml")
count, seed = int(config["data"]["samples"]), int(config["data"]["seed"])
interval_hours = int(config["data"]["interval_hours"])
rng = np.random.default_rng(seed)
hours = (np.arange(count) * interval_hours) % 24
days = (np.arange(count) * interval_hours / 24) % 365
daylight = np.maximum(0.0, np.sin(np.pi * (hours - 6) / 12))
season = np.sin(2 * np.pi * (days - 172) / 365)
swdown = (760 + 110 * season) * daylight
t2 = 284 + 10 * season + 5 * np.sin(2 * np.pi * (hours - 14) / 24) + rng.normal(0, 0.8, count)
q2 = np.clip(0.0065 + 0.004 * (season + 1) / 2 + 0.0012 * daylight + rng.normal(0, 0.0003, count), 0.001, None)
ug = 5.5 + 1.8 * np.sin(2 * np.pi * days / 9) + rng.normal(0, 0.5, count)
vg = 2.0 + 1.5 * np.cos(2 * np.pi * days / 13) + rng.normal(0, 0.5, count)
u10 = 0.62 * ug + rng.normal(0, 0.45, count); v10 = 0.62 * vg + rng.normal(0, 0.45, count)
hfx = 12 + 150 * daylight * (0.75 + 0.25 * season) + rng.normal(0, 5, count)
lh = 25 + 110 * daylight * (0.8 + 0.2 * season) + rng.normal(0, 5, count)
pblh = np.clip(170 + 1200 * daylight * (0.75 + 0.25 * season) + rng.normal(0, 45, count), 80, 1800)
ust = np.clip(0.12 + 0.0014 * hfx + 0.022 * np.hypot(u10, v10), 0.05, 1.2)
tsk = t2 + 1.5 + 4.0 * daylight; tslb = 283 + 7.0 * season
smois = np.clip(0.25 + 0.04 * np.sin(2 * np.pi * days / 40) - 0.025 * season + rng.normal(0, 0.008, count), 0.08, 0.45)
grdflx = 0.12 * hfx - 8 * (1 - daylight); glw = 285 + 2.2 * (t2 - 273.15) + 450 * q2
x = np.column_stack((q2, t2, u10, v10, grdflx, swdown, glw, lh, hfx, pblh, ust, tsk, tslb, smois, ug, vg)).astype(np.float32)
heights = np.array([10, 35, 70, 110, 160, 220, 290, 370, 460, 560, 680, 820, 980, 1160, 1370, 1600, 1880], dtype=np.float32)
z = heights[None, :]; mix = np.exp(-z / pblh[:, None])
u = ug[:, None] + (u10 - ug)[:, None] * mix + 0.18 * np.sin(z / 260)
v = vg[:, None] + (v10 - vg)[:, None] * mix + 0.14 * np.cos(z / 300)
w = 0.20 * daylight[:, None] * np.sin(np.pi * np.minimum(z / pblh[:, None], 1)) - 0.015 * (1 - daylight[:, None]) + rng.normal(0, 0.008, (count, 17))
tk = t2[:, None] - 0.0062 * z + 0.9 * mix * daylight[:, None]
qv = np.clip(q2[:, None] * np.exp(-z / 2100) * (0.88 + 0.12 * mix) + 1e-4 * smois[:, None], 1e-5, None)
y = np.stack((u, v, w, tk, qv), axis=-1).astype(np.float32)
y += rng.normal(size=y.shape).astype(np.float32) * np.array([0.035, 0.035, 0.003, 0.04, 0.000015], dtype=np.float32)
n_train, n_val = int(0.70 * count), int(0.15 * count)
slices = {"train": slice(0, n_train), "val": slice(n_train, n_train + n_val), "test": slice(n_train + n_val, count)}
output = ROOT / config["paths"]["data"]; output.parent.mkdir(parents=True, exist_ok=True)
payload = {"heights_m": heights, "input_names": np.asarray(INPUT_NAMES), "output_names": np.asarray(OUTPUT_NAMES)}
for name, section in slices.items():
payload.update({f"x_{name}": x[section], f"y_{name}": y[section], f"time_{name}": (np.arange(count)[section] * interval_hours).astype(np.int64), f"pblh_{name}": pblh[section].astype(np.float32)})
np.savez_compressed(output, **payload)
print(f"saved {output}: x={x.shape}, y={y.shape}, split={[len(range(*s.indices(count))) for s in slices.values()]}")
if __name__ == "__main__":
main()