File size: 4,673 Bytes
b300acf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | """Generate dimensionally faithful synthetic EOF sequences and precipitation fields."""
import json
import sys
from pathlib import Path
import numpy as np
import yaml
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
from model.cesm_seasonal_ml import CLASS_NAMES, DATA_FORMAT_VERSION, canonical_patterns
VARIABLES = ["SST_TP", "SST_WP", "SST_IO", "SST_NP", "VP200_PW", "U200_NP", "Z500_ENP"]
def make_manifest(size, max_lag):
names = []
lag = 0
while len(names) < size:
for variable in VARIABLES:
for eof in range(1, 5):
names.append(f"{variable}_EOF{eof}_Lag{lag}")
if len(names) == size:
return names
lag = (lag + 1) % (max_lag + 1)
return names
def main():
config = yaml.safe_load((ROOT / "conf/config.yaml").read_text())
rng = np.random.default_rng(config["seed"])
sizes = [config["data"]["train_size"], config["data"]["validation_size"], config["data"]["test_size"]]
n = sum(sizes)
height, width = config["data"]["grid"]
patterns = canonical_patterns(height, width)
eof = np.zeros((2, n, 12, 28), dtype=np.float32)
precipitation = np.zeros((2, n, height, width), dtype=np.float32)
latent_labels = np.zeros((2, n), dtype=np.int64)
yy, xx = np.mgrid[-1:1:complex(height), -1:1:complex(width)]
texture = np.sin(2.5 * xx) * np.cos(2 * yy)
for season in range(2):
enso = rng.normal(size=n)
west_pacific = 0.45 * enso + rng.normal(scale=0.9, size=n)
circulation = -0.55 * enso + 0.35 * west_pacific + rng.normal(scale=0.75, size=n)
score = np.stack([enso - circulation, -enso + 0.2 * west_pacific, enso + 0.3 * circulation, -enso - west_pacific], axis=1)
score += rng.normal(scale=0.7 if season else 0.9, size=score.shape)
labels = score.argmax(axis=1)
latent_labels[season] = labels
for month in range(12):
persistence = 0.82 ** (11 - month)
eof[season, :, month] = rng.normal(scale=0.7, size=(n, 28))
eof[season, :, month, 0] += persistence * enso * 2.0
eof[season, :, month, 4] += persistence * west_pacific * 1.4
eof[season, :, month, 16] += persistence * circulation * 1.2
amplitude = rng.uniform(0.8, 1.5, size=n)
noise = rng.normal(scale=0.38, size=(n, height, width))
precipitation[season] = amplitude[:, None, None] * patterns[labels] + 0.18 * score.max(axis=1)[:, None, None] * texture + noise
rf_manifest = make_manifest(103, 12)
nn_manifest = make_manifest(336, 11)
nn_manifest += [f"engineering_interaction_{index:03d}" for index in range(1, 81)]
rf_features = np.empty((2, n, 103), dtype=np.float32)
nn_features = np.empty((2, n, 416), dtype=np.float32)
flat = eof.reshape(2, n, -1)
rf_features[:] = flat[:, :, :103]
# The reported 416-vector cannot be reconstructed from the published manifest.
# Preserve all 336 sequence values and add deterministic interaction features.
nn_features[:, :, :336] = flat
nn_features[:, :, 336:] = flat[:, :, :80] * flat[:, :, 28:108]
split = np.repeat(np.array([0, 1, 2], dtype=np.int8), sizes)
years = np.concatenate([np.arange(1920, 1920 + sizes[0]), np.arange(1920, 1920 + sizes[1]), np.arange(1981, 1981 + sizes[2])]).astype(np.int32)
output = ROOT / config["data"]["path"]
output.parent.mkdir(parents=True, exist_ok=True)
np.savez_compressed(output, format_version=DATA_FORMAT_VERSION, seasons=np.array(["NDJ", "JFM"]), eof_sequence=eof,
rf_features=rf_features, nn_features=nn_features, precipitation=precipitation,
latent_labels=latent_labels, split=split, years=years,
latitude=np.linspace(31, 49, height), longitude=np.linspace(-125, -102, width),
rf_manifest=np.array(rf_manifest), nn_manifest=np.array(nn_manifest), class_names=np.array(CLASS_NAMES))
metadata = {"samples": n, "split_sizes": sizes, "grid": [height, width], "eof_sequence": [2, n, 12, 28],
"rf_features": [2, n, 103], "nn_features": [2, n, 416], "synthetic": True,
"grid_status": "engineering assumption, not a paper-reported dimension",
"manifest_status": "first 336 structured placeholders plus 80 unique engineering_interaction_* dimension-preserving assumptions; not an exact paper feature list"}
(output.parent / "metadata.json").write_text(json.dumps(metadata, indent=2) + "\n")
print(f"data={output.relative_to(ROOT)} samples={n} grid={height}x{width}")
if __name__ == "__main__":
main()
|