Working file-by-file gets tedious. x4c.Timeseries indexes a whole post-processed
case directory and gives you variables by name.
import os
import numpy as np
import xarray as xr
import matplotlib.pyplot as plt
import nc_time_axis # registers the cftime axis converter for matplotlib
import x4c
x4c.set_style('journal')
# after set_style: it resets rcParams from matplotlibrc defaults, which includes the
# backend, so assert the inline backend last or figures are never captured
%matplotlib inline
# The tutorial runs against a reduced copy of a real CESM case. It is published as a
# GitHub Release asset rather than committed, so the first call downloads it into
# ~/.cache/x4c (override with $X4C_CACHE_DIR) and later calls reuse it. Set
# $X4C_SAMPLE_DIR to point at a copy you already have.
case_dir = x4c.fetch_sample_data(case='cesm1', verbose=False)
casename = os.path.basename(case_dir)
print('x4c', x4c.__version__)x4c 2026.6.11
Opening a case¶
Point it at the case root. x4c walks <comp>/proc/tseries/<freq>/ and parses the
CESM filename convention to find out which variables exist, in which component, and
in which history stream.
cesm_ver=1 turns on the CESM1 timestamp shift for every variable it loads.
case = x4c.Timeseries(
case_dir,
grid_dict={'atm': 'ne16np4', 'ocn': 'g16'},
cesm_ver=1,
)>>> case.root_dir: ~/.cache/x4c/sample_data/cesm1_sample_data/b.e13.B1850C5.ne16_g16.icesm131_d18O_fixer.Miocene.3xCO2.005
>>> case.path_pattern: comp/proc/tseries/*/casename.hstr.vn.timespan.nc
>>> case.grid_dict: {'atm': 'ne16np4', 'ocn': 'g16', 'lnd': 'ne16np4', 'rof': 'ne16np4', 'ice': 'g16'}
>>> case.casename: b.e13.B1850C5.ne16_g16.icesm131_d18O_fixer.Miocene.3xCO2.005
>>> case.paths["atm"]["cam.h0"] created
>>> case.paths["ocn"]["pop.h"] created
>>> case.paths["lnd"]["clm2.h0"] created
>>> case.paths["ice"]["cice.h"] created
>>> case.vns["atm"]["cam.h0"] created
>>> case.vns["ocn"]["pop.h"] created
>>> case.vns["lnd"]["clm2.h0"] created
>>> case.vns["ice"]["cice.h"] created
What it found¶
case.vns maps component and history stream to the available variables.
for comp in sorted(case.vns):
for hstr, vns in case.vns[comp].items():
print(f'{comp:4s} {hstr:9s} {len(vns):2d} variables: {", ".join(sorted(vns))}')atm cam.h0 28 variables: FLNT, FSNT, ICEFRAC, LANDFRAC, LWCF, PRECC, PRECL, PRECRC_H216Or, PRECRC_H218Or, PRECRC_H2Or, PRECRC_HDOr, PRECRL_H216OR, PRECRL_H218OR, PRECRL_H2OR, PRECRL_HDOR, PRECSC_H216Os, PRECSC_H218Os, PRECSC_H2Os, PRECSC_HDOs, PRECSL_H216OS, PRECSL_H218OS, PRECSL_H2OS, PRECSL_HDOS, PS, SWCF, T, TS, U
ice cice.h 1 variables: aice
lnd clm2.h0 2 variables: RAIN, TSA
ocn pop.h 6 variables: MOC, R18O, SALT, SSH, TEMP, XMXL
case.paths holds the files behind each variable. TS is split across two
five-year files, which x4c will open together when asked for the whole record.
for f in case.paths['atm']['cam.h0']['TS']:
print(os.path.basename(f))b.e13.B1850C5.ne16_g16.icesm131_d18O_fixer.Miocene.3xCO2.005.cam.h0.TS.000101-000512.nc
b.e13.B1850C5.ne16_g16.icesm131_d18O_fixer.Miocene.3xCO2.005.cam.h0.TS.000601-001012.nc
Which component holds a variable?¶
You rarely need to say. get_comp_hstr is what load uses internally to resolve a
name; it only needs disambiguating when the same name exists in two streams.
for vn in ['TS', 'SSH', 'aice', 'TEMP']:
print(f'{vn:6s} ->', case.get_comp_hstr(vn))TS -> [('atm', 'cam.h0')]
SSH -> [('ocn', 'pop.h')]
aice -> [('ice', 'cice.h')]
TEMP -> [('ocn', 'pop.h')]
The sample is a reduced case¶
The bundled data is deliberately small, and not every variable covers the same span. Two-dimensional fields cover years 1-10; three-dimensional fields are shorter, because a single 3-D POP record is larger than an entire 2-D timeseries.
for comp, hstr, vn in [('atm', 'cam.h0', 'TS'), ('atm', 'cam.h0', 'T'),
('ocn', 'pop.h', 'SSH'), ('ocn', 'pop.h', 'TEMP')]:
files = [os.path.basename(f) for f in case.paths[comp][hstr][vn]]
spans = [f.split('.')[-2] for f in files]
print(f'{comp}/{vn:5s} {len(files)} file(s): {", ".join(spans)}')atm/TS 2 file(s): 000101-000512, 000601-001012
atm/T 2 file(s): 000101-000112, 000201-000212
ocn/SSH 2 file(s): 000101-000512, 000601-001012
ocn/TEMP 1 file(s): 000101-000112