Skip to content

Commit a68ef2c

Browse files
authored
Fix 6hr, 3hr issue (#497)
1 parent f66a62d commit a68ef2c

4 files changed

Lines changed: 58 additions & 15 deletions

File tree

src/access_moppy/base.py

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,24 @@ def __setitem__(self, key, value):
296296
def __repr__(self):
297297
return repr(self.ds)
298298

299+
def _is_fx_variable(self) -> bool:
300+
"""Return True when compound_name corresponds to a fixed-field CMIP table."""
301+
if not self.compound_name:
302+
return False
303+
304+
table_id = self.compound_name.split(".", 1)[0].lower()
305+
return table_id.endswith("fx")
306+
307+
def _squeeze_fx_singleton_time(self) -> None:
308+
"""Drop UM-style singleton time axis for fixed (fx) variables."""
309+
if (
310+
self.ds is not None
311+
and self._is_fx_variable()
312+
and "time" in self.ds.dims
313+
and self.ds.sizes.get("time") == 1
314+
):
315+
self.ds = self.ds.isel(time=0, drop=True)
316+
299317
def load_dataset(self, required_vars: Optional[List[str]] = None):
300318
"""
301319
Load dataset from input files or use provided xarray objects with optional frequency validation.
@@ -363,11 +381,30 @@ def load_dataset(self, required_vars: Optional[List[str]] = None):
363381
# Original file-based loading logic
364382
def _preprocess(ds):
365383
ds = ds[list(required_vars & set(ds.data_vars))]
366-
# Drop auxiliary UM time coordinates (time_0, time_1) that differ
367-
# across files. Without this, xr.open_mfdataset's join='outer'
368-
# unions every distinct value into a growing dimension, inflating
369-
# the Dask task graph to several GiB before any computation starts.
370-
aux_time_coords = [c for c in ("time_0", "time_1") if c in ds]
384+
# Canonicalize UM auxiliary time dimensions (time_0/time_1) to
385+
# a single "time" axis when the selected variables use exactly
386+
# one such axis. Keep that primary axis and drop the unused one.
387+
selected_data_vars = list(ds.data_vars)
388+
used_time_dims = {
389+
dim
390+
for var in selected_data_vars
391+
for dim in ds[var].dims
392+
if dim.startswith("time")
393+
}
394+
primary_time_dim = "time" if "time" in used_time_dims else None
395+
if primary_time_dim is None and len(used_time_dims) == 1:
396+
primary_time_dim = next(iter(used_time_dims))
397+
398+
if primary_time_dim and primary_time_dim != "time":
399+
ds = ds.rename({primary_time_dim: "time"})
400+
used_time_dims.discard(primary_time_dim)
401+
used_time_dims.add("time")
402+
403+
aux_time_coords = [
404+
c
405+
for c in ("time_0", "time_1")
406+
if c in ds.coords and c not in used_time_dims
407+
]
371408
if aux_time_coords:
372409
ds = ds.drop_vars(aux_time_coords)
373410
return ds
@@ -383,8 +420,9 @@ def _preprocess(ds):
383420
if required_vars
384421
else list(_probe.data_vars)
385422
)
386-
_has_time = (required_vars is None or "time" in required_vars) and any(
387-
"time" in _probe[v].dims for v in _probe_target_vars
423+
_has_time = any(
424+
any(dim.startswith("time") for dim in _probe[v].dims)
425+
for v in _probe_target_vars
388426
)
389427

390428
# Validate frequency consistency and CMIP6 compatibility before concatenation
@@ -394,7 +432,7 @@ def _preprocess(ds):
394432
# Time-independent variables typically have "fx" (fixed) in their table ID
395433
is_time_independent = (
396434
self.compound_name and "fx" in self.compound_name.lower()
397-
) or "time" not in _probe_dims
435+
) or not any(dim.startswith("time") for dim in _probe_dims)
398436

399437
if is_time_independent:
400438
logger.debug(
@@ -496,11 +534,13 @@ def _preprocess(ds):
496534
if required_vars:
497535
vars_to_keep = [v for v in required_vars if v in self.ds.data_vars]
498536
self.ds = self.ds[vars_to_keep]
499-
# UM source files always include a time dimension (size=1) even for
500-
# static fields. Drop it so downstream CMOR processing sees the
501-
# expected (lat, lon) shape rather than (time=1, lat, lon).
502-
if "time" in self.ds.dims:
503-
self.ds = self.ds.isel(time=0, drop=True)
537+
# UM source files can include a time=1 axis for static fields.
538+
# Keep squeeze behavior centralized in _squeeze_fx_singleton_time().
539+
540+
# UM source files can carry time=1 for fixed fields even when loaded
541+
# through the time-aware branch. Squeeze once here before any downstream
542+
# frequency handling, rechunking, or missing-value normalization.
543+
self._squeeze_fx_singleton_time()
504544

505545
# Apply temporal resampling if enabled and needed
506546
if self.enable_resampling and self.compound_name:

src/access_moppy/driver.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,9 @@ def __init__(
482482
"CFmon",
483483
"CFday",
484484
"3hr",
485+
"3hrPt",
485486
"6hrPlev",
487+
"6hrPlevPt",
486488
"E1hr",
487489
"Eday",
488490
"fx",
@@ -553,7 +555,7 @@ def __init__(
553555
raise ValueError(
554556
f"Unsupported CMIP table '{table}' in compound_name '{compound_name}'. "
555557
f"Supported legacy CMIP6 tables — "
556-
f"atmosphere: ('Amon', 'Lmon', 'LImon', 'Emon', 'AERmon', 'AERday', 'day', 'CFmon', 'CFday', '3hr', '6hrPlev', 'E1hr', 'Eday', 'fx', 'Efx', 'atmos'), "
558+
f"atmosphere: ('Amon', 'Lmon', 'LImon', 'Emon', 'AERmon', 'AERday', 'day', 'CFmon', 'CFday', '3hr', '3hrPt', '6hrPlev', '6hrPlevPt', 'E1hr', 'Eday', 'fx', 'Efx', 'atmos'), "
557559
f"ocean: ('Oyr', 'Oday', 'Omon', 'Ofx'), "
558560
f"sea-ice: ('SImon', 'SIday'). "
559561
f"MIP CMOR table prefixes are also supported: "

src/access_moppy/examples/batch_config_esm1-6_cmip7_baseline.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ variables:
109109
- seaIce.siconc.tavg-u-hxy-u.mon.glb # SImon.siconc
110110
- seaIce.simass.tavg-u-hxy-si.mon.glb # SImon.simass
111111
# - seaIce.snd.tavg-u-hxy-sn.mon.glb # SImon.sisnthick — NOT MAPPED
112-
- seaIce.ts.tavg-u-hxy-si.mon.glb # SImon.sitemptop
112+
# - seaIce.ts.tavg-u-hxy-si.mon.glb # SImon.sitemptop
113113
- seaIce.sithick.tavg-u-hxy-si.mon.glb # SImon.sithick
114114
- seaIce.sitimefrac.tavg-u-hxy-sea.mon.glb # SImon.sitimefrac
115115
- seaIce.siu.tavg-u-hxy-si.mon.glb # SImon.siu

src/access_moppy/utilities.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,7 @@ def parse_cmip6_table_frequency(compound_name: str) -> pd.Timedelta:
694694
"SIday": pd.Timedelta(days=1),
695695
# Additional frequency tables
696696
"3hr": pd.Timedelta(hours=3),
697+
"3hrPt": pd.Timedelta(hours=3),
697698
"6hr": pd.Timedelta(hours=6),
698699
"day": pd.Timedelta(days=1),
699700
"mon": pd.Timedelta(days=30),

0 commit comments

Comments
 (0)