File size: 4,288 Bytes
87f2bd3 | 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 | """Generate 21 structured, physically interpretable full-grid response pairs."""
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.climemu_s2l import FORMAT_VERSION, GRID_SHAPE
SCENARIOS = [
("PDRMIP_2xCO2", 2.5, "global", 0, 0),
("PDRMIP_3xCH4", 1.5, "global", 0, 0),
("PDRMIP_10xCFC12", 1.1, "global", 0, 0),
("PDRMIP_solar", 1.0, "global", 0, 0),
("PDRMIP_5xSO4", -1.6, "global", 0, 0),
("PDRMIP_10xBC", 1.2, "global", 0, 0),
("PDRMIP_10xSO4_Europe", -1.0, "regional", 52, 15),
("PDRMIP_10xSO4_Asia", -1.1, "regional", 35, 105),
("PDRMIP_preindustrial_SO4", 0.8, "global", 0, 0),
("ECLIPSE_CH4_minus20", -0.7, "global", 0, 0),
("ECLIPSE_2xCO2", 2.3, "global", 0, 0),
("ECLIPSE_BC_minus100", -0.6, "global", 0, 0),
("ECLIPSE_SO2_minus100", 1.0, "global", 0, 0),
("ECLIPSE_CO_minus100", -0.45, "global", 0, 0),
("KASOAR_SO2_NHML_minus100", 0.75, "regional", 42, 30),
("KASOAR_BC_NHML_minus100", -0.55, "regional", 42, 30),
("KASOAR_SO2_China_minus100", 0.70, "regional", 34, 105),
("KASOAR_SO2_EastAsia_minus100", 0.65, "regional", 40, 125),
("KASOAR_SO2_Europe_minus100", 0.62, "regional", 52, 15),
("KASOAR_SO2_US_minus100", 0.58, "regional", 40, 260),
("PDRMIP_SO4_Asia_alt", -0.72, "regional", 25, 80),
]
def wrapped_distance(longitude, centre):
return (longitude - centre + 180.0) % 360.0 - 180.0
def main():
config = yaml.safe_load((ROOT / "conf/config.yaml").read_text())
ny, nx = map(int, config["data"]["grid"])
if (ny, nx) != GRID_SHAPE or len(SCENARIOS) != 21:
raise ValueError("protocol requires exactly 21 scenarios on a 145x192 grid")
latitude = np.linspace(-90.0, 90.0, ny, dtype=np.float64)
longitude = np.linspace(0.0, 360.0, nx, endpoint=False, dtype=np.float64)
lat, lon = np.meshgrid(latitude, longitude, indexing="ij")
polar = np.sin(np.deg2rad(lat)) ** 4
land_wave = np.cos(np.deg2rad(2.0 * lon - 0.6 * lat)) * np.cos(np.deg2rad(lat)) ** 2
short_fields, long_fields = [], []
for index, (scenario_id, amplitude, forcing_type, centre_lat, centre_lon) in enumerate(SCENARIOS):
local = np.exp(-0.5 * ((lat - centre_lat) / 16.0) ** 2
-0.5 * (wrapped_distance(lon, centre_lon) / 28.0) ** 2)
if forcing_type == "global":
local = 0.25 * np.cos(np.deg2rad(lat)) ** 2
phase = 2.0 * np.pi * index / len(SCENARIOS)
forcing_region = amplitude * local
global_warming = amplitude * (0.52 + 0.34 * polar)
circulation = 0.13 * amplitude * np.cos(np.deg2rad(lat * 2.0) + phase) * np.sin(np.deg2rad(lon) - phase)
short = global_warming + 0.72 * forcing_region + 0.10 * amplitude * land_wave + circulation
zonal = short.mean(axis=1, keepdims=True)
remote_east = np.roll(short, nx // 5, axis=1)
cross_equatorial = np.flip(zonal, axis=0)
planetary_wave = np.cos(np.deg2rad(lon * 2.0 + centre_lon)) * np.cos(np.deg2rad(lat))
long = (1.42 * short + 0.28 * remote_east + 0.23 * cross_equatorial
+ short.mean() * (0.48 + 0.62 * polar) + 0.09 * amplitude * planetary_wave)
short_fields.append(short.astype(np.float32))
long_fields.append(long.astype(np.float32))
short_response = np.stack(short_fields)
long_response = np.stack(long_fields)
if short_response.shape != (21, 145, 192) or not np.isfinite(long_response).all():
raise ValueError("invalid synthetic response fields")
output = ROOT / config["data"]["path"]
output.parent.mkdir(parents=True, exist_ok=True)
np.savez_compressed(output, format_version=np.array(FORMAT_VERSION),
scenario_ids=np.asarray([item[0] for item in SCENARIOS]),
forcing_amplitude=np.asarray([item[1] for item in SCENARIOS], dtype=np.float32),
latitude_deg=latitude.astype(np.float32), longitude_deg=longitude.astype(np.float32),
short_response=short_response, long_response=long_response)
print(f"data={output.relative_to(ROOT)} shape={short_response.shape} features={ny * nx}")
if __name__ == "__main__":
main()
|