"""Phase 1 step 1: ONE minimal ECDS s2s-reforecasts retrieval + GRIB verification. Auth pattern matches the user's verified working script (My_swers.md): cdsapi.Client(url="https://ecds.ecmwf.int/api") -> key read from ~/.cdsapirc (or CDSAPI_KEY env). The existing key authenticates against ECDS when the url is overridden in code, so ~/.cdsapirc need not be changed. Request schema is the native ECDS syntax from that script (origin, year/month/day, hyear/hmonth/hday, level_type, variable=paramId, forecast_type=control_forecast, leadtime_hour="start_end", data_format, area=[N,W,S,E], grid). The concrete request lives in config/ecds_minimal_request.json (reproduced from ECDS). Verifies the returned GRIB with ecCodes CLI (grib_ls/grib_get) + cdo (D6); cfgrib is NOT required. Keeps the original GRIB. """ import json import os import subprocess import traceback ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) OUT = os.path.join(ROOT, "data", "raw", "ecmwf_s2s_1p5deg") os.makedirs(OUT, exist_ok=True) target = os.path.join(OUT, "s2s_refc_cf_min.grib") REQ_FILE = os.path.join(ROOT, "config", "ecds_minimal_request.json") DATASET = "s2s-reforecasts" ECDS_URL = "https://ecds.ecmwf.int/api" def load_request(): with open(REQ_FILE) as f: req = json.load(f) req.pop("_comment", None) return req def have_key(): if os.environ.get("CDSAPI_KEY"): return True rc = os.environ.get("CDSAPI_RC", os.path.expanduser("~/.cdsapirc")) if os.path.exists(rc): return any(l.strip().startswith("key") for l in open(rc)) return False def verify_grib(path): out = {} checks = { "grib_ls": ["grib_ls", path], "grib_get": ["grib_get", "-p", "shortName,paramId,name,units,gridType,Ni,Nj," "iDirectionIncrementInDegrees,jDirectionIncrementInDegrees," "latitudeOfFirstGridPointInDegrees,longitudeOfFirstGridPointInDegrees," "dataDate,dataTime,stepRange,stepType,typeOfLevel,level," "marsType,marsStream,marsClass,numberOfDataPoints", path], "cdo_sinfon": ["cdo", "sinfon", path], } for name, cmd in checks.items(): try: p = subprocess.run(cmd, capture_output=True, text=True, timeout=120) out[name] = (p.stdout or p.stderr)[:3000] except Exception as e: out[name] = f"{type(e).__name__}: {e}" return out status = {"dataset": DATASET, "endpoint": ECDS_URL, "outcome": None} try: req = load_request() status["request"] = req if not have_key(): raise RuntimeError("no cdsapi key found (CDSAPI_KEY or ~/.cdsapirc)") import cdsapi client = cdsapi.Client(url=ECDS_URL) print(f"submitting minimal ECDS retrieval: {DATASET} (grid {req.get('grid')})", flush=True) client.retrieve(DATASET, req).download(target) ok = os.path.exists(target) and os.path.getsize(target) > 0 status["outcome"] = "downloaded" if ok else "no_file" status["bytes"] = os.path.getsize(target) if ok else 0 if ok: status["target"] = target status["grib_verification"] = verify_grib(target) except Exception as e: status["outcome"] = "error" status["message"] = f"{type(e).__name__}: {e}" status["traceback"] = traceback.format_exc()[-2000:] with open(os.path.join(ROOT, "metadata", "ecmwf_minimal_retrieval_status.json"), "w") as f: json.dump(status, f, indent=2) print(json.dumps({k: status.get(k) for k in ("outcome", "endpoint", "bytes", "message")}, indent=2), flush=True) if status.get("grib_verification"): print("\n--- grib_ls ---\n" + status["grib_verification"].get("grib_ls", ""), flush=True) print("\n--- grib_get ---\n" + status["grib_verification"].get("grib_get", ""), flush=True)