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()