|
1 | | -# cmip7_prep/cli.py |
2 | | -import typer |
| 1 | + |
| 2 | +"""Simple CLI entry points for cmip7_prep (argparse-based).""" |
| 3 | +from __future__ import annotations |
| 4 | +import argparse |
3 | 5 | from pathlib import Path |
4 | | -from cmip7_prep.dreq import DReq |
5 | | -from cmip7_prep.regrid import RegridderPool |
6 | | -from cmip7_prep.cmor_writer import CmorSession |
7 | | -from cmip7_prep.vertical import to_plev19 |
| 6 | +import xarray as xr |
| 7 | +import numpy as np |
8 | 8 |
|
9 | | -app = typer.Typer(help="Prepare CESM output for CMIP7 with CMOR.") |
| 9 | +from cmip7_prep.regrid import regrid_to_1deg |
| 10 | +from cmip7_prep.cmor_writer import CmorSession |
10 | 11 |
|
11 | | -@app.command() |
12 | | -def make_target(out: Path = Path("grids/target_1deg.nc")): |
13 | | - """Build a canonical 1° grid template with lat/lon, bounds, cell_area.""" |
14 | | - import xarray as xr, numpy as np |
| 12 | +def make_target(out: Path) -> None: |
| 13 | + """Create a canonical 1° grid template with lat/lon and (placeholder) bounds.""" |
15 | 14 | lat = np.arange(-89.5, 90.5, 1.0) |
16 | 15 | lon = np.arange(0.5, 360.5, 1.0) |
17 | | - ds = xr.Dataset( |
18 | | - dict(), |
19 | | - coords=dict(lat=("lat", lat), lon=("lon", lon)) |
20 | | - ) |
21 | | - # add bounds + area (simple spherical approx or from ESMF Mesh) |
22 | | - # ... (fill) ... |
| 16 | + ds = xr.Dataset(coords={"lat": ("lat", lat), "lon": ("lon", lon)}) |
23 | 17 | ds.to_netcdf(out) |
24 | 18 |
|
25 | | -@app.command() |
26 | | -def build_weights(cam_grid: Path, target: Path="grids/target_1deg.nc", outdir: Path="grids/weights"): |
27 | | - """(Optional) Document how you produced weights with TempestRemap; store here.""" |
28 | | - # We keep the CLI hook mainly for provenance notes. |
29 | | - # Actual weights are produced offline with TempestRemap (see README). |
30 | | - |
31 | | -@app.command() |
32 | 19 | def prepare( |
33 | 20 | var: str, |
34 | 21 | in_files: list[Path], |
35 | | - realm: str, # 'Amon', 'Lmon', 'Omon', etc. |
36 | | - dreq_export: Path, # Airtable export CSV/JSON |
37 | | - mapping_yaml: Path="mapping/cesm_to_cmip7.yaml", |
38 | | - cmor_tables: Path="Tables", # CMOR JSON (CMIP7-or-6 as applicable) |
39 | | - outdir: Path="out" |
40 | | -): |
41 | | - """Main entry: regrid (if needed) and CMOR-write one variable.""" |
42 | | - import xarray as xr |
43 | | - dreq = DReq(dreq_export, mapping_yaml) |
44 | | - vdef = dreq.lookup(realm, var) # all required metadata |
45 | | - |
| 22 | + realm: str, |
| 23 | + mapping_yaml: Path, |
| 24 | + cmor_tables: Path, |
| 25 | + dataset_json: Path, |
| 26 | + outdir: Path, |
| 27 | +) -> None: |
| 28 | + """Regrid (if needed) and CMOR-write a single variable (thin wrapper demo).""" |
46 | 29 | ds = xr.open_mfdataset([str(p) for p in in_files], combine="by_coords", use_cftime=True) |
47 | | - |
48 | | - # Regrid for ATM/LND only |
49 | | - if realm[0] in ("A","L"): |
50 | | - from cmip7_prep.regrid import regrid_to_1deg |
51 | | - ds[var] = regrid_to_1deg(var, ds, method=vdef.regrid_method) |
52 | | - |
53 | | - # Vertical levels if needed (e.g., plev19) |
54 | | - if vdef.requires_plev19: |
55 | | - ds = to_plev19(ds, var, vdef) |
56 | | - |
57 | | - # CMOR write |
58 | | - with CmorSession(tables_path=cmor_tables, dataset_attrs=dreq.dataset_attrs()) as cm: |
59 | | - cm.write_variable(ds, var, vdef, outdir=Path(outdir)) |
| 30 | + da = ds[var] |
| 31 | + ds_tmp = xr.Dataset({var: da}) |
| 32 | + da1 = regrid_to_1deg(ds_tmp, var) |
| 33 | + ds_out = xr.Dataset({var: da1}) |
| 34 | + with CmorSession(tables_path=str(cmor_tables), dataset_json=str(dataset_json)) as cm: |
| 35 | + vdef = type("VDef", (), {"name": var, "realm": realm, "units": da.attrs.get("units", "")})() |
| 36 | + cm.write_variable(ds_out, var, vdef, outdir=outdir) |
| 37 | + |
| 38 | +def main(argv: list[str] | None = None) -> int: |
| 39 | + p = argparse.ArgumentParser(prog="cmip7-prep") |
| 40 | + sub = p.add_subparsers(dest="cmd", required=True) |
| 41 | + |
| 42 | + p_make = sub.add_parser("make-target") |
| 43 | + p_make.add_argument("out", type=Path) |
| 44 | + p_make.set_defaults(func=lambda a: make_target(a.out)) |
| 45 | + |
| 46 | + p_prep = sub.add_parser("prepare") |
| 47 | + p_prep.add_argument("--var", required=True) |
| 48 | + p_prep.add_argument("--in-file", "-i", action="append", required=True, type=Path) |
| 49 | + p_prep.add_argument("--realm", required=True) |
| 50 | + p_prep.add_argument("--mapping-yaml", required=True, type=Path) |
| 51 | + p_prep.add_argument("--cmor-tables", required=True, type=Path) |
| 52 | + p_prep.add_argument("--dataset-json", required=True, type=Path) |
| 53 | + p_prep.add_argument("--outdir", default=Path("out"), type=Path) |
| 54 | + p_prep.set_defaults(func=lambda a: prepare( |
| 55 | + a.var, a.in_file, a.realm, a.mapping_yaml, a.cmor_tables, a.dataset_json, a.outdir |
| 56 | + )) |
| 57 | + |
| 58 | + args = p.parse_args(argv) |
| 59 | + args.func(args) |
| 60 | + return 0 |
60 | 61 |
|
61 | 62 | if __name__ == "__main__": |
62 | | - app() |
63 | | - |
| 63 | + raise SystemExit(main()) |
0 commit comments