Skip to content

Commit 7a096da

Browse files
committed
all pre-commit now passing
1 parent 1483a4d commit 7a096da

File tree

9 files changed

+187
-73
lines changed

9 files changed

+187
-73
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,9 @@ cython_debug/
182182
.abstra/
183183

184184
# Visual Studio Code
185-
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
185+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
186186
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
187-
# and can be added to the global gitignore or merged into this file. However, if you prefer,
187+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
188188
# you could uncomment the following to ignore the entire vscode folder
189189
# .vscode/
190190

.pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ disable=
1515
max-line-length=100
1616

1717
[TYPECHECK]
18-
ignored-modules=xarray,xesmf,cmor
18+
ignored-modules=xarray,xesmf,cmor,numpy,geocat.comp
1919
ignored-classes=cmor

cmip7_prep/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
21
"""CMIP7 preparation toolkit: regridding and CMOR writing for CESM outputs."""
2+
33
__all__ = ["regrid", "cmor_writer"]

cmip7_prep/cli.py

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
21
"""Simple CLI entry points for cmip7_prep (argparse-based)."""
2+
33
from __future__ import annotations
44
import argparse
55
from pathlib import Path
@@ -9,13 +9,15 @@
99
from cmip7_prep.regrid import regrid_to_1deg
1010
from cmip7_prep.cmor_writer import CmorSession
1111

12+
1213
def make_target(out: Path) -> None:
1314
"""Create a canonical 1° grid template with lat/lon and (placeholder) bounds."""
1415
lat = np.arange(-89.5, 90.5, 1.0)
1516
lon = np.arange(0.5, 360.5, 1.0)
1617
ds = xr.Dataset(coords={"lat": ("lat", lat), "lon": ("lon", lon)})
1718
ds.to_netcdf(out)
1819

20+
1921
def prepare(
2022
*,
2123
var: str,
@@ -26,43 +28,75 @@ def prepare(
2628
outdir: Path,
2729
) -> None:
2830
"""Regrid (if needed) and CMOR-write a single variable (thin wrapper demo)."""
29-
ds = xr.open_mfdataset([str(p) for p in in_files], combine="by_coords", use_cftime=True)
31+
ds = xr.open_mfdataset(
32+
[str(p) for p in in_files], combine="by_coords", use_cftime=True
33+
)
3034
da = ds[var]
3135
ds_tmp = xr.Dataset({var: da})
3236
da1 = regrid_to_1deg(ds_tmp, var)
3337
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", "")})()
38+
with CmorSession(
39+
tables_path=str(cmor_tables), dataset_json=str(dataset_json)
40+
) as cm:
41+
vdef = type(
42+
"VDef",
43+
(),
44+
{"name": var, "realm": realm, "units": da.attrs.get("units", "")},
45+
)()
3646
cm.write_variable(ds_out, var, vdef, outdir=outdir)
3747

48+
3849
def main(argv: list[str] | None = None) -> int:
3950
"""Entry point for the cmip7_prep command-line interface."""
4051
p = argparse.ArgumentParser(prog="cmip7-prep")
4152
sub = p.add_subparsers(dest="cmd", required=True)
4253

4354
p_make = sub.add_parser("make-target", help="Write a simple 1° lat/lon grid file.")
44-
p_make.add_argument("out", type=Path, help="Output NetCDF path for the target grid.")
55+
p_make.add_argument(
56+
"out", type=Path, help="Output NetCDF path for the target grid."
57+
)
4558
p_make.set_defaults(func=lambda a: make_target(a.out))
4659

47-
p_prep = sub.add_parser("prepare", help="Regrid one variable and write CMOR output.")
60+
p_prep = sub.add_parser(
61+
"prepare", help="Regrid one variable and write CMOR output."
62+
)
4863
p_prep.add_argument("--var", required=True, help="Variable name in input files.")
49-
p_prep.add_argument("--in-file", "-i", action="append", required=True, type=Path, help="Input files (repeatable).")
64+
p_prep.add_argument(
65+
"--in-file",
66+
"-i",
67+
action="append",
68+
required=True,
69+
type=Path,
70+
help="Input files (repeatable).",
71+
)
5072
p_prep.add_argument("--realm", required=True, help="CMOR realm/table, e.g., Amon.")
51-
p_prep.add_argument("--cmor-tables", required=True, type=Path, help="Path to CMOR Tables directory.")
52-
p_prep.add_argument("--dataset-json", required=True, type=Path, help="Path to cmor_dataset.json.")
53-
p_prep.add_argument("--outdir", default=Path("out"), type=Path, help="Output directory for CMORized files.")
54-
p_prep.set_defaults(func=lambda a: prepare(
55-
var=a.var,
56-
in_files=a.in_file,
57-
realm=a.realm,
58-
cmor_tables=a.cmor_tables,
59-
dataset_json=a.dataset_json,
60-
outdir=a.outdir,
61-
))
73+
p_prep.add_argument(
74+
"--cmor-tables", required=True, type=Path, help="Path to CMOR Tables directory."
75+
)
76+
p_prep.add_argument(
77+
"--dataset-json", required=True, type=Path, help="Path to cmor_dataset.json."
78+
)
79+
p_prep.add_argument(
80+
"--outdir",
81+
default=Path("out"),
82+
type=Path,
83+
help="Output directory for CMORized files.",
84+
)
85+
p_prep.set_defaults(
86+
func=lambda a: prepare(
87+
var=a.var,
88+
in_files=a.in_file,
89+
realm=a.realm,
90+
cmor_tables=a.cmor_tables,
91+
dataset_json=a.dataset_json,
92+
outdir=a.outdir,
93+
)
94+
)
6295

6396
args = p.parse_args(argv)
6497
args.func(args)
6598
return 0
6699

100+
67101
if __name__ == "__main__":
68102
raise SystemExit(main())

cmip7_prep/cmor_dataset.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@
2525
"institution": "National Center for Atmospheric Research, Boulder, CO, USA",
2626
"tracking_id": "",
2727
"title": "CESM CMIP7 output prepared with cmip7_prep"
28-
}
28+
}

cmip7_prep/cmor_writer.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
21
"""Thin CMOR wrapper used by cmip7_prep.
32
43
This module centralizes CMOR session setup and writing so that the rest of the
54
pipeline can stay xarray-first. It supports either a dataset JSON file (preferred)
65
or directly injected global attributes, and creates axes based on the coordinates
76
present in the provided dataset.
87
"""
8+
99
from __future__ import annotations
1010

1111
from contextlib import AbstractContextManager
@@ -88,10 +88,14 @@ def _define_axes(self, ds: xr.Dataset, vdef: Any) -> list[int]:
8888
cal = time.attrs.get("calendar", "standard")
8989
if np.issubdtype(time.dtype, np.datetime64):
9090
# convert to numeric time using CF units if needed
91-
tvals = xr.conventions.times.encode_cf_datetime(time.values, t_units, calendar=cal)
91+
tvals = xr.conventions.times.encode_cf_datetime(
92+
time.values, t_units, calendar=cal
93+
)
9294
else:
9395
tvals = time.values
94-
axes.append(cmor.axis(table_entry="time", units=str(t_units), coord_vals=tvals))
96+
axes.append(
97+
cmor.axis(table_entry="time", units=str(t_units), coord_vals=tvals)
98+
)
9599

96100
# Vertical axis (pressure or model levels)
97101
levels_info = getattr(vdef, "levels", None)
@@ -121,7 +125,9 @@ def _define_axes(self, ds: xr.Dataset, vdef: Any) -> list[int]:
121125
table_entry = "alev"
122126
if isinstance(levels_info, dict):
123127
table_entry = levels_info.get("axis_entry", table_entry)
124-
axes.append(cmor.axis(table_entry=table_entry, units="1", coord_vals=lev.values))
128+
axes.append(
129+
cmor.axis(table_entry=table_entry, units="1", coord_vals=lev.values)
130+
)
125131

126132
# Latitude / Longitude
127133
if "lat" in ds.coords and "lon" in ds.coords:
@@ -152,7 +158,9 @@ def _define_axes(self, ds: xr.Dataset, vdef: Any) -> list[int]:
152158
# public API
153159
# -------------------------
154160

155-
def write_variable(self, ds: xr.Dataset, varname: str, vdef: Any, outdir: Path) -> None:
161+
def write_variable(
162+
self, ds: xr.Dataset, varname: str, vdef: Any, outdir: Path
163+
) -> None:
156164
"""Write one variable from `ds` to a CMOR-compliant NetCDF file.
157165
158166
Parameters
@@ -162,8 +170,10 @@ def write_variable(self, ds: xr.Dataset, varname: str, vdef: Any, outdir: Path)
162170
varname : str
163171
Name of the variable in `ds` to CMORize.
164172
vdef : Any
165-
An object with fields: ``name``, ``realm``, optional ``units``, ``positive``, and optional
166-
``levels`` dict (see :meth:`_define_axes`). This is typically a light-weight holder.
173+
An object with fields: ``name``, ``realm``,
174+
optional ``units``, ``positive``, and optional
175+
``levels`` dict (see :meth:`_define_axes`).
176+
This is typically a light-weight holder.
167177
outdir : Path
168178
Output directory for the CMORized NetCDF file.
169179
"""
@@ -182,8 +192,12 @@ def write_variable(self, ds: xr.Dataset, varname: str, vdef: Any, outdir: Path)
182192

183193
axes_ids = self._define_axes(ds, vdef)
184194
units = getattr(vdef, "units", "")
185-
var_id = cmor.variable(getattr(vdef, "name", varname), units, axes_ids,
186-
positive=getattr(vdef, "positive", None))
195+
var_id = cmor.variable(
196+
getattr(vdef, "name", varname),
197+
units,
198+
axes_ids,
199+
positive=getattr(vdef, "positive", None),
200+
)
187201

188202
# Optional variable attributes (e.g., cell_methods)
189203
if getattr(vdef, "cell_methods", None):

cmip7_prep/dreq.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
r"""Utilities for reading and querying a CMIP Data Request export.
32
43
Supports Airtable-style JSON (with a top-level ``records`` list) and CSV exports.
@@ -12,6 +11,7 @@
1211
>>> 'tas' in vars_amon
1312
True
1413
"""
14+
1515
from __future__ import annotations
1616

1717
import csv
@@ -60,8 +60,24 @@ class DReq:
6060

6161
# Heuristics for common column names across exports
6262
_DEFAULT_TABLE_KEYS = ["Table", "table", "CMOR table", "CMOR_table"]
63-
_DEFAULT_GROUP_KEYS = ["Group", "Groups", "Bucket", "MIP bucket", "Baseline group", "Notes", "Tags"]
64-
_DEFAULT_SHORT_NAME_KEYS = ["Short name", "short_name", "Variable", "Name", "name", "CMIP Variable", "Compound name"]
63+
_DEFAULT_GROUP_KEYS = [
64+
"Group",
65+
"Groups",
66+
"Bucket",
67+
"MIP bucket",
68+
"Baseline group",
69+
"Notes",
70+
"Tags",
71+
]
72+
_DEFAULT_SHORT_NAME_KEYS = [
73+
"Short name",
74+
"short_name",
75+
"Variable",
76+
"Name",
77+
"name",
78+
"CMIP Variable",
79+
"Compound name",
80+
]
6581

6682
def __init__(
6783
self,
@@ -76,7 +92,9 @@ def __init__(
7692
self.rows: List[Dict[str, Any]] = self._read(self.path)
7793
self.table_keys: List[str] = table_keys or self._DEFAULT_TABLE_KEYS
7894
self.group_keys: List[str] = group_keys or self._DEFAULT_GROUP_KEYS
79-
self.short_name_keys: List[str] = short_name_keys or self._DEFAULT_SHORT_NAME_KEYS
95+
self.short_name_keys: List[str] = (
96+
short_name_keys or self._DEFAULT_SHORT_NAME_KEYS
97+
)
8098
self.group_patterns = _compile_patterns(group_regexes)
8199

82100
# ---------------------------

0 commit comments

Comments
 (0)