CrocoDash.raw_data_access.datasets package

Contents

CrocoDash.raw_data_access.datasets package#

Submodules#

CrocoDash.raw_data_access.datasets.cesm_ocean_output module#

Data Access Module -> CESM ocean output

Two products, split because they have different variable/coordinate naming conventions (and so need different MOM6ForcingProduct metadata):

  • CESM_POP_OUTPUT: CESM-POP tseries output (CESM-HR FOSI, CESM2-LENS2)

  • CESM_MOM_OUTPUT: native MOM6 output (e.g. parent-run history/diag_table output for nesting a child domain)

Both share the same lower-level file-discovery/subsetting helpers below.

class CrocoDash.raw_data_access.datasets.cesm_ocean_output.CESM_MOM_OUTPUT#

Bases: MOM6ForcingProduct

boundary_fill_method = 'regional_mom6'#
calendar = Calendar(cf='noleap', cesm='NO_LEAP', mom6='noleap')#
depth_coord = 'z_l'#
description = 'Native MOM6 output (full history/diagnostic files, or diag_table-extracted cross-section slices) for use as IC/OBC forcing - e.g. for nesting a child domain inside an outer/parent MOM6 run.'#
eta_var_name = 'zos'#
static get_mom6_output_data(dates: list, lat_min, lat_max, lon_min, lon_max, name=None, output_folder=PosixPath('.'), output_filename=None, variables=['zos', 'thetao', 'so', 'uo', 'vo'], dataset_path=None, file_glob='*.nc', time_var_name='time', buffer_deg=1.5, preview=False)#
static get_mom6_single_variable_data(dates: list, lat_min, lat_max, lon_min, lon_max, name=None, output_folder=PosixPath('.'), output_filename=None, variables=['zos', 'thetao', 'so', 'uo', 'vo'], dataset_path=None, member=None, date_format: str = '%Y%m%d', regex='(\\d{6,8})-(\\d{6,8})', delimiter='.', preview=False)#
logger = <Logger CESM_MOM_OUTPUT (INFO)>#
product_name = 'cesm_mom_output'#
time_units = 'days'#
time_var_name = 'time'#
tracer_lat_coord = 'yh'#
tracer_lon_coord = 'xh'#
tracer_var_names = {'salt': 'so', 'temp': 'thetao'}#
tracer_x_coord = 'xh'#
tracer_y_coord = 'yh'#
u_lat_coord = 'yh'#
u_lon_coord = 'xq'#
u_var_name = 'uo'#
u_x_coord = 'xq'#
u_y_coord = 'yh'#
v_lat_coord = 'yq'#
v_lon_coord = 'xh'#
v_var_name = 'vo'#
v_x_coord = 'xh'#
v_y_coord = 'yq'#
class CrocoDash.raw_data_access.datasets.cesm_ocean_output.CESM_POP_OUTPUT#

Bases: MOM6ForcingProduct

boundary_fill_method = 'regional_mom6'#
calendar = Calendar(cf='noleap', cesm='NO_LEAP', mom6='noleap')#
delimiter = '.'#
depth_coord = ['z_t', 'z_t_150m']#
description = 'CESM ocean output (POP2 grid) for use as IC and OBC, including CESM-HR FOSI and CESM2 Large Ensemble (LENS2)'#
eta_var_name = 'SSH'#
static get_cesm_single_variable_data(dates: list, lat_min, lat_max, lon_min, lon_max, name=None, output_folder=PosixPath('.'), output_filename=None, variables=['SSH', 'TEMP', 'SALT', 'VVEL', 'UVEL'], dataset_path='/glade/campaign/collections/cmip/CMIP6/CESM-HR/FOSI_BGC/HR/g.e22.TL319_t13.G1850ECOIAF_JRA_HR.4p2z.001/ocn/proc/tseries/month_1', member=None, date_format: str = '%Y%m%d', regex='(\\d{6,8})-(\\d{6,8})', delimiter='.', lat_name='TLAT', lon_name='TLONG', preview=False)#
logger = <Logger CESM_POP_OUTPUT (INFO)>#
marbl_var_names = {'ALK': 'ALK', 'ALK_ALT_CO2': 'ALK_ALT_CO2', 'DIC': 'DIC', 'DIC_ALT_CO2': 'DIC_ALT_CO2', 'DOC': 'DOC', 'DOCr': 'DOCr', 'DON': 'DON', 'DONr': 'DONr', 'DOP': 'DOP', 'DOPr': 'DOPr', 'Fe': 'Fe', 'Lig': 'Lig', 'NH4': 'NH4', 'NO3': 'NO3', 'O2': 'O2', 'PO4': 'PO4', 'SiO3': 'SiO3', 'coccoC': 'coccoC', 'coccoCaCO3': 'coccoCaCO3', 'coccoChl': 'coccoChl', 'coccoFe': 'coccoFe', 'coccoP': 'coccoP', 'diatC': 'diatC', 'diatChl': 'diatChl', 'diatFe': 'diatFe', 'diatP': 'diatP', 'diatSi': 'diatSi', 'diazC': 'diazC', 'diazChl': 'diazChl', 'diazFe': 'diazFe', 'diazP': 'diazP', 'mesozooC': 'mesozooC', 'microzooC': 'microzooC', 'spC': 'spC', 'spChl': 'spChl', 'spFe': 'spFe', 'spP': 'spP'}#
product_name = 'cesm_pop_output'#
time_units = 'days since 1850-01-01'#
time_var_name = 'time'#
tracer_lat_coord = 'TLAT'#
tracer_lon_coord = 'TLONG'#
tracer_var_names = {'salt': 'SALT', 'temp': 'TEMP'}#
tracer_x_coord = 'nlon'#
tracer_y_coord = 'nlat'#
u_lat_coord = 'TLAT'#
u_lon_coord = 'TLONG'#
u_var_name = 'UVEL'#
u_x_coord = 'nlon'#
u_y_coord = 'nlat'#
v_lat_coord = 'TLAT'#
v_lon_coord = 'TLONG'#
v_var_name = 'VVEL'#
v_x_coord = 'nlon'#
v_y_coord = 'nlat'#
z_unit_conversion = 0.01#
CrocoDash.raw_data_access.datasets.cesm_ocean_output.bbox_mask(ds, lat_min, lat_max, lon_min, lon_max, lat_name='lat', lon_name='lon', buffer_deg=0)#

Boolean lat/lon mask for subsetting a dataset, handling both 0-360 and -180-180 longitude conventions.

lon_min/lon_max name a contiguous arc running eastward from lon_min, the way Grid.get_bounding_boxes returns one. An arc that crosses the antimeridian ends past 180 – 170 to 190, say – because no pair of in-range ends can describe one. Normalising each end on its own turns that into 170 to -170 and selects nothing, so measure each source longitude as an offset eastward from the start of the arc instead: a point is inside when its offset is no wider than the arc. Taking that offset modulo 360 puts both the source’s convention and the crossing to rest, so neither needs a branch of its own.

CrocoDash.raw_data_access.datasets.cesm_ocean_output.convert_cftime_to_numeric(ds, time_var_name='time', apply_month_shift=False, calendar: Calendar = Calendar(cf='noleap', cesm='NO_LEAP', mom6='noleap'))#

Converts a cftime time coordinate to numeric (days since 1850-01-01, noleap) for safe NetCDF serialization; a no-op if the coordinate isn’t cftime. apply_month_shift is specific to the CESM-POP tseries convention (average- endpoint timestamp labeling) and should not be used for native MOM6 output.

CrocoDash.raw_data_access.datasets.cesm_ocean_output.drop_extra_cftime_vars(ds)#
CrocoDash.raw_data_access.datasets.cesm_ocean_output.first_value(da_var)#
CrocoDash.raw_data_access.datasets.cesm_ocean_output.get_date_range_from_filename(path, regex)#
CrocoDash.raw_data_access.datasets.cesm_ocean_output.parse_dataset(variable_names: list[str], dataset_path: str | Path, start_date: str, end_date: str, date_format: str = '%Y%m%d', regex='(\\d{6,8})-(\\d{6,8})', space_character='.') → dict#

Parses the dataset to find variable names and their corresponding file paths.

Parameters:
  • variable_names (list[str]) – List of variable names to search for.

  • dataset_path (str | xr.Dataset | Path) – Path to the dataset (or folder with dataset)

  • space_character (str) – Character that separates words in variable names in the filenames. Default is “.”.

Returns:

A dictionary with variable names as keys and their file paths as values.

Return type:

dict

CrocoDash.raw_data_access.datasets.cesm_ocean_output.read_single_variable_tseries_data(dates, lat_min, lat_max, lon_min, lon_max, output_folder, output_filename, variables, dataset_path, member, date_format, regex, delimiter, lat_name, lon_name, preview, apply_month_shift, grid_coords=None)#

Shared implementation for reading single-variable-per-file (tseries) output for a bounding box, used by both CESM_POP_OUTPUT.get_cesm_single_variable_data and CESM_MOM_OUTPUT.get_mom6_single_variable_data - the CESM tseries file convention (one variable per file series, date range in the filename) shows up for both CESM-POP and native MOM6 output.

grid_coords optionally maps a variable name to its own (lat_name, lon_name) pair, overriding lat_name/lon_name for that variable - needed for MOM6’s C-grid, where uo/vo live on different horizontal dims than the tracer grid.

CrocoDash.raw_data_access.datasets.cesm_ocean_output.subset_dataset(variable_info: dict, output_path: str | Path, lat_min: float, lat_max: float, lon_min: float, lon_max: float, lat_name='lat', lon_name='lon', dates=None, preview: bool = False, apply_month_shift: bool = True, grid_coords: dict | None = None) → None#

Subsets (and merges) the dataset based on the provided variable names and geographical bounds into the output path.

Parameters:
  • variable_info (dict) – A dictionary with variable names as keys and their file paths as values.

  • output_path (str | Path) – The path where the subsetted dataset will be saved.

  • lat_min (float) – Minimum latitude for subsetting.

  • lat_max (float) – Maximum latitude for subsetting.

  • lon_min (float) – Minimum longitude for subsetting.

  • lon_max (float) – Maximum longitude for subsetting.

  • lat_name (str) – Default latitude coordinate name, used for any variable not listed in grid_coords. Default is “lat”.

  • lon_name (str) – Default longitude coordinate name, used for any variable not listed in grid_coords. Default is “lon”.

  • dates (tuple) – Just used for the file naming

  • preview (bool) – If True, only previews the subsetting without saving. Default is False.

  • apply_month_shift (bool) – Apply the CESM-POP tseries average-endpoint timestamp correction. Default True (matches CESM-POP); set False for native MOM6 tseries output.

  • grid_coords (dict | None) – Optional {var_name: (lat_name, lon_name)} override - needed for MOM6’s C-grid, where uo/vo live on different horizontal dims than the tracer grid, so a single lat_name/lon_name pair can’t mask every variable correctly.

CrocoDash.raw_data_access.datasets.cesm_ocean_output.subtract_month(dt)#
CrocoDash.raw_data_access.datasets.cesm_ocean_output.validate_dataset_path(dataset_path)#

CrocoDash.raw_data_access.datasets.cesm_ww3_jra module#

Data Access Module -> CESM-WW3-JRA (global WAVEWATCH III spectral database)

Serves full 2D wave spectra E(f, theta) for a regional case’s open boundaries out of a JRA-forced, global, wave-only CESM/WW3 run’s 6-hourly netCDF restarts. It is the third ww3_obc_product_name option alongside reference_waves (synthetic, reference.py) and era5_wave_spectra (CDS/ECMWF, era5.py), and unlike either of those it needs no network access and no credentials – just read access to the run directory on GLADE.

Why it exists: the regional cases this feeds are themselves JRA-forced, so their boundary spectra come from the same winds that drive their interior, which ERA5 cannot offer. The default database was produced by wi_jra.glo1p0.001, a 1-degree global WW3 run (grid ww3glo1p0, 360x160, cyclic in x) started cold on 2018-11-01, whose useful record therefore begins about 2018-12-01, after 2-4 weeks of spin-up.

What this module does NOT do is any physics: a WW3 restart already holds the full spectrum. The work is reading it out in the right shape, and the five conventions that takes were read out of WW3/model/src rather than guessed:

  1. va index order kk = (ik-1)*nth + ith – frequency-major, so the

    600 va variables reshape to (nk, nth) (wav_restart_mod.F90:190-197, w3gridmd.F90:1334, ww3_bounc.F90:643)

  2. frequency axis f_k = FR1 * XFR**(k-1), Hz (w3gridmd.F90:1313)

  3. va is ACTION E(f,th) = va * 2*pi*sigma / Cg(f, depth), giving

    m2 s rad-1 (w3iopomd.F90:1231-1240)

  4. direction axis WW3’s internal TH is Cartesian, CCW from east,

    direction-of-propagation; what a spectrum file carries is mod(450 - th_deg, 360), i.e. the nautical “to” direction (ww3_ounp.F90:3245, the exact inverse of ww3_bounc.F90:581). Hence direction_convention = DIRECTION_TO below – this is the one product of the three that is certain about its convention, because both ends of it are the same code base.

  5. direction ORDER keep WW3’s own index order (90, 75, …, 0, 345,

    …, 105 – descending, then wrapping, so not monotonic). ww3_bounc sets SPCONV from THETA(1) vs TH(1) (ww3_bounc.F90:629); that order makes it False and the copy bit-exact. Sorting the axis ascending would flip SPCONV to True and force a needless W3CSPC remap.

Those five were then checked numerically, twice. First on the producing run’s own HS history field: Hs rebuilt from va0001..va0600 matched it to a max absolute error of 0.0001 m over 3991 wet points. Then end to end on what this module actually emits, over an Agulhas-sized window on 2019-06-05:

  • Hs integrated from the emitted E(f, theta) is 0.984-0.9998 of the Hs the same restart gives through WW3’s own integration. The deficit is exactly the unresolved high-frequency tail, which WW3 adds analytically (FTE) and a spectrum file does not carry – ww3_bounc re-adds its own.

  • The emitted direction axis agrees with the run’s exported Stokes drift – which points unambiguously where the waves are going, with no convention to argue about – to a median 23 degrees, against 157 degrees for the “coming from” alternative. The axis is “to”, as conventions 4 and 5 say it should be.

Conversion 3 needs the local water depth, which the restart does not carry (it has mapsta but no coordinates and no bathymetry). It comes from the static MOM6-style topography file that the same grid-generation step wrote next to the WW3 grid preprocessor inputs – verified to be exactly the <grid>_bottom.inp that built mod_def.ww3, and carrying min_depth as a global attribute. Because the producing run sets input%forcing%water_levels = “F”, WW3’s dynamic depth equals that static bathymetry exactly, so this is not an approximation. It would stop being exact for a database whose run turns water levels on.

Calendar: the database runs NO_LEAP, because its own JRA v1.5 forcing only exists as a noleap dataset, while the regional cases that consume it run GREGORIAN. Every date is labelled correctly on both sides except that the database has no YYYY-02-29 at all. A request covering one is served YYYY-02-28’s spectra for the matching time of day and stamped with the requested 02-29, so the consumer sees an unbroken gregorian series. Repeating 02-28 rather than interpolating across the gap is deliberate: a repeated day is a self-consistent sea state, where blending 02-28 and 03-01 bin by bin can produce a spectrum that matches neither.

Sea ice is why land is marked as NaN rather than as zero. A cell that WW3 has iced over carries exactly zero energy in every bin, and at high latitudes a whole boundary can be like that: the north edge of a Bering Sea domain at 67 N is all zero every January and carries real spectra again by August. That is a valid boundary condition – no wave energy enters through ice – and it has to reach ww3_bounc as real zero-valued stations, not be mistaken for land and dropped, which would leave ww3_bounc extrapolating swell into the ice from open water hundreds of kilometres south.

Two caveats that belong to the database, not to this reader, and that matter when judging the spectra it serves:

  • The producing run has stub ocean (SOCN), so no currents and therefore no current-wave refraction. That matters most in exactly the places a strong boundary current crosses a domain edge.

  • Its sea ice is DICE%SSMI, a CLIMATOLOGY rather than observed interannual ice. The ice edge is therefore climatological: fine for mid-latitude domains, a real caveat for polar ones.

class CrocoDash.raw_data_access.datasets.cesm_ww3_jra.CESM_WW3_JRA#

Bases: WW3ForcingProduct

calendar = Calendar(cf='standard', cesm='GREGORIAN', mom6='gregorian')#
description = "Full 2D wave spectra E(f, theta) for WW3 open boundaries, read out of a JRA-forced global wave-only CESM/WW3 run's 6-hourly netCDF restarts on GLADE -- boundary waves driven by the same JRA winds as the regional case's own interior, with no network access or credentials required. The default database is the 1-degree `wi_jra.glo1p0.001` run (useful record from ~2018-12-01, after spin-up)."#
direction_convention = 'to'#
static get_cesm_ww3_jra_spectra(dates: list, lat_min, lat_max, lon_min, lon_max, name=None, output_folder=PosixPath('.'), output_filename='cesm_ww3_jra_spectra.nc', variables=None, database_root='/glade/campaign/cgd/oce/projects/CROCODILE/data/wi_jra.glo1p0.001/restarts', grid_file='/glade/campaign/cgd/oce/projects/CROCODILE/data/wi_jra.glo1p0.001/grid/ocean_topog_ww3glo1p0.nc', case_name=None, fr1=0.04118, xfr=1.1, direction_offset=0.0, buffer_deg=1.5, min_stations=2, max_buffer_deg=10.0)#

Extract a boundary window’s spectra and write them as NetCDF.

variables is unused – a WW3 restart’s 600 va variables already are the whole spectrum – and is kept only to satisfy ForcingProduct’s required_args contract.

buffer_deg pads the requested box before indexing. Some padding is not optional here: a boundary’s box from mom6_forge.Grid.get_bounding_boxes is a near-zero-width strip (a “north” boundary spans the domain’s longitudes at essentially one latitude), and on a 1-degree database such a strip can contain no cell centre at all. If fewer than min_stations wet cells fall in the padded window, the padding grows by one degree at a time up to max_buffer_deg before giving up – ww3_bounc blends boundary points from whatever stations it is given by inverse distance, so two real neighbours beat one exact-but-lonely hit.

Returns the written NetCDF path.

land_marker = 'nan'#
logger = <Logger CESM_WW3_JRA (INFO)>#
product_name = 'CESM-WW3-JRA'#
time_units = 'hours'#
time_var_name = 'time'#
classmethod validate_method(method_name, **kwargs)#

Default validation just makes a toy call with a temporary directory.

Returns True if the call succeeds, False if it raises. The call’s own result is not passed on: whatever it wrote went to the temporary directory, which is removed before this returns.

CrocoDash.raw_data_access.datasets.cesm_ww3_jra.action_to_energy(va, sigma, cg)#

Convert WW3’s stored action density to variance density E(f, theta).

E = va * 2*pi*sigma / Cg (w3iopomd.F90:1231-1240), m2 s rad-1.

Parameters:
  • va ((..., nk, nth) action density as stored in the restart.)

  • sigma ((nk,) radian frequencies.)

  • cg ((…, nk) group velocities, broadcastable against va’s leading) – dimensions.

CrocoDash.raw_data_access.datasets.cesm_ww3_jra.available_range(database_root, case_name)#

(first, last) restart timestamp present in the database, as Timestamps.

Read from filenames, not from the files. The producing run may still be advancing, so this is deliberately re-read on every call rather than baked into the product metadata as a fixed end date.

CrocoDash.raw_data_access.datasets.cesm_ww3_jra.discover_case_name(database_root)#

Infer the CESM case name from the restarts in database_root.

Restarts are named <case>.ww3.r.<date>-<seconds>.nc, so the case name is everything before the first .ww3.r.. Inferring it beats making the caller repeat a name that is already written on every file in the directory, and a directory holding more than one case is ambiguous enough to be worth refusing outright.

CrocoDash.raw_data_access.datasets.cesm_ww3_jra.group_velocity(sigma, depth, gravity=9.806)#

Linear-theory group velocity Cg(sigma, depth), m s-1.

Solves sigma**2 = g*k*tanh(k*d) for the wavenumber by Newton iteration from a deep-water first guess, then Cg = 0.5*sigma/k * (1 + 2kd/sinh(2kd)).

Parameters:
  • sigma ((nk,) radian frequencies.)

  • depth ((...,) water depths, m, strictly positive.)

Return type:

(…, nk) group velocities.

CrocoDash.raw_data_access.datasets.cesm_ww3_jra.requested_timestamps(dates)#

Every 6-hourly restart stamp of every whole day in dates.

Whole-day inclusive on both ends: a plain date_range(..., freq="6h") would stop at the last day’s 00:00 and drop its 06/12/18Z steps, so the end day is extended to 18:00 first. Same “every step of every requested day” convention reference.py and era5.py use, which is also what obc.py’s filename-based coverage check assumes.

CrocoDash.raw_data_access.datasets.cesm_ww3_jra.restart_filename(case_name, stamp)#

Name of the restart holding stamp, applying the NO_LEAP mapping.

The database has no 29 February (see the module docstring), so a gregorian 02-29 is served by 02-28’s file at the same time of day. Every other date maps to its own label.

CrocoDash.raw_data_access.datasets.cesm_ww3_jra.spectral_axes(nk, nth, fr1=0.04118, xfr=1.1, direction_offset=0.0)#

Build a WW3 grid’s frequency and direction axes from its five spectral parameters. Pure, so the conventions above are testable without a restart.

Returns:

  • frequency ((nk,) float64, Hz – fr1 * xfr**(k-1).)

  • sigma ((nk,) float64, rad s-1 – 2*pi*frequency, needed by) – group_velocity and action_to_energy.

  • direction ((nth,) float64, degrees, nautical “to” convention, in WW3’s) – own (non-monotonic) index order – see conventions 4 and 5 in the module docstring.

CrocoDash.raw_data_access.datasets.cice_output module#

Data Access Module -> CICE restart

A global CICE restart carries no time or lat/lon coordinates of its own – just raw ni/nj/ncat state arrays on the model’s native tripole grid index space. This module index-subsets a restart to a regional domain’s bounding box using the companion CICE grid file (tlon/tlat, stored in radians despite their degrees_* attrs) to locate the matching (nj, ni) window, so the result can be used as a cold-start initial condition for a regional CICE case. The same window’s tlon/tlat/ulon/ulat are attached to the output (in degrees) so downstream regridding has real coordinates without re-opening the grid file.

class CrocoDash.raw_data_access.datasets.cice_output.CICE_RESTART#

Bases: CICEForcingProduct

calendar = Calendar(cf='standard', cesm='GREGORIAN', mom6='gregorian')#
depth_coord = None#
description = "Global CICE restart file, index-subset to a regional domain's bounding box for use as a cold-start initial condition."#
static get_cice_restart_subset(dates: list, lat_min, lat_max, lon_min, lon_max, name=None, output_folder=PosixPath('.'), output_filename='cice_restart_subset.nc', variables=None, restart_path='please_provide_a_path', grid_path='please_provide_a_path', buffer_deg=1.5, preview=False)#
logger = <Logger CICE_RESTART (INFO)>#
product_name = 'cice_restart'#
time_units = None#
time_var_name = None#
tracer_var_names = {}#
tracer_x_coord = 'ni'#
tracer_y_coord = 'nj'#
u_var_name = 'uvel'#
u_x_coord = 'ni'#
u_y_coord = 'nj'#
v_var_name = 'vvel'#
v_x_coord = 'ni'#
v_y_coord = 'nj'#
CrocoDash.raw_data_access.datasets.cice_output.find_cice_index_window(grid_path, lat_min, lat_max, lon_min, lon_max, buffer_deg=1.5)#

Locate the (ni_index, nj_min, nj_max) window on a CICE tripole grid (T-point tlon/tlat) covering the given lat/lon bounding box.

Longitude is resolved via mom6_forge’s longitude_slicer against the grid’s row 0 – every row of a CICE tripole grid is identical and uniformly spaced in longitude below the displaced-pole fold, and row 0 (the grid’s southern edge) is always below it. Latitude doesn’t need the same periodicity handling (no wraparound), so it’s a plain argwhere over tlat restricted to the resolved longitude window, which also naturally follows the curvature of latitude lines near the fold.

Known limitation: this returns a contiguous ni index range and can’t represent a window that straddles the tripole seam itself – not expected for realistic regional domains, since CESM’s tripole grids place both displaced poles over land specifically to avoid this.

CrocoDash.raw_data_access.datasets.empty_products module#

class CrocoDash.raw_data_access.datasets.empty_products.CESMInputData#

Bases: BaseProduct

description = 'The CESM Input SVN repo holds all files CrocoDash exposes publicly themselves at the following repo link'#
logger = <Logger CESMInputData (INFO)>#
product_name = 'CESM Inputdata'#
class CrocoDash.raw_data_access.datasets.empty_products.TPXO#

Bases: BaseProduct

description = 'TPXO (TOPEX/POSEIDON Global Tidal Ocean I think) is a public tidal model dataset'#
logger = <Logger TPXO (INFO)>#
product_name = 'tpxo'#

CrocoDash.raw_data_access.datasets.era5 module#

Data Access Module -> ERA5 (true 2D wave spectra)

Fetches ERA5’s true 2D wave energy-density spectrum E(f, theta) – ECMWF parameter 251.140, “wave” stream – via CDS’s MARS-style reanalysis-era5-complete access, NOT the bulk/integrated wave stats (significant height, mean direction, mean period) available on the friendlier reanalysis-era5-single-levels dataset.

Real pulls against cds.climate.copernicus.eu (2026-08-03) confirmed the actual on-the-wire shape, which is not a plain multi-dim GRIB field:

  • Requesting just param: “251.140” returns ONE message per timestep, all pinned at directionNumber=1/frequencyNumber=1 – MARS does not expand the spectrum unless explicitly told to. Getting the full 2D spectrum requires explicit direction/frequency MARS keys (1-based bin indices, not physical values): “direction”: “1/to/24”, “frequency”: “1/to/30”. That turns one timestep into 24*30=720 separate GRIB messages (one per direction/frequency bin), confirmed via eccodes message iteration.

  • Real direction bins (coefsFirst GRIB key, degrees) and frequency bins (coefsSecond GRIB key, Hz) follow ECMWF’s standard WAM discretization: 24 directions 15 degrees apart starting at 7.5 degrees, 30 frequencies starting at ~0.0345 Hz on a geometric ratio of 1.1 – fixed for every message in a file, read once from the first message.

  • Values are base-10 log-encoded (observed values like -2.98, consistent with ECMWF’s documented convention of storing log10 of spectral density to compress dynamic range) with a per-message missingValue sentinel (9999.0 seen in practice) marking bins with no energy – decoded here as 10**value where present, 0.0 where missing. The vast majority of (direction, frequency) messages in a real pull ARE the missing sentinel (597 of 720 in a 1-hour Bering Sea test pull) – only bins actually carrying energy get a real value.

  • cfgrib’s default xr.open_dataset engine does NOT reconstruct this correctly: without an explicit hint that directionNumber/frequencyNumber vary, it silently keeps only the first (dn=1, fn=1) message per time/space point and drops the rest – exactly what was observed on an unexpanded (single-bin) file, and would still be a silent underdecode on an expanded one. Because of this, decoding here bypasses cfgrib entirely and reads GRIB messages directly via eccodes.

  • “grid”: “0.5/0.5” in the request (added below) forces ECMWF’s native Reduced Lat-Lon wave grid to be regridded onto a regular lat/lon grid server-side – required because MARS/MIR cannot crop (“area”) the native representation directly (raises ReducedLL::croppedRepresentation() not supported). “data_format” replaces the deprecated “format” request key.

Given all of the above, get_era5_2d_spectra below does not hand back a raw, unopened GRIB file the way GLORYS/GLOFAS/CICE_RESTART’s accessmethods do – there is no generic downstream tool that decodes this file’s shape correctly, so decoding into a clean, real-dimensioned NetCDF file (time, latitude, longitude, frequency, direction) happens here, at fetch time, before handing off to extract_forcings/ww3.py.

One assumption is carried through UNVERIFIED by direct sanity check (no independent physical validation was done against a known reference spectrum, just documented ECMWF convention): direction is the “coming from” convention, clockwise from true north – matches what extract_forcings/ww3.py’s write_ww3_boundary_spectrum already expects/ converts internally.

Requires a cdsapi config pointed at cds.climate.copernicus.eu with reanalysis-era5-complete access accepted on the CDS site – NOT the ~/.cdsapirc already on this system, which points at ewds.climate.copernicus.eu (the Early Warning Data Store used by the existing GLOFAS product’s CEMS dataset).

class CrocoDash.raw_data_access.datasets.era5.ERA5_WAVE_SPECTRA#

Bases: WW3ForcingProduct

calendar = Calendar(cf='standard', cesm='GREGORIAN', mom6='gregorian')#
description = "ERA5's true 2D wave energy-density spectrum E(f, theta) (ECMWF param 251.140, wave stream) from Copernicus CDS's reanalysis-era5-complete dataset -- NOT the bulk/integrated wave stats (significant height, mean direction, mean period) available on reanalysis-era5-single-levels."#
direction_convention = 'coming_from'#
static get_era5_2d_spectra(dates: list, lat_min, lat_max, lon_min, lon_max, name=None, output_folder=PosixPath('.'), output_filename='era5_spectra.nc', variables=None, cdsapi_rc_path='~/.cdsapirc_era5')#

Downloads ERA5’s true 2D wave spectra for the requested date range and bounding box using the cdsapi package, decodes the GRIB response, and writes the result as NetCDF at output_folder/output_filename. Note that variables is unused – param 251.140 already is the whole 2D spectrum – it’s kept only to satisfy ForcingProduct’s required_args contract.

Returns the NetCDF path, not the intermediate GRIB – downstream code (extract_forcings/ww3.py) never touches GRIB/eccodes directly.

land_marker = 'zero'#
logger = <Logger ERA5_WAVE_SPECTRA (INFO)>#
product_name = 'era5_wave_spectra'#
time_units = 'hours'#
time_var_name = 'time'#
CrocoDash.raw_data_access.datasets.era5.build_era5_spectra_request(dates: list, lat_min, lat_max, lon_min, lon_max, buffer_deg=1.0) → dict#

Pure builder for a CDS reanalysis-era5-complete (MARS-style) request dict for ERA5’s 2D wave spectra (param 251.140, wave stream) – no network I/O, so it’s unit-testable without cdsapi credentials.

buffer_deg pads the requested box before slicing – needed because a boundary’s box from mom6_forge.Grid.get_bounding_boxes is a near-zero- width strip (e.g. “north” spans the full domain longitude at essentially one latitude), and a literal zero-height CDS area request could miss every real ERA5 grid point. Same reasoning GLORYS’s own accessmethods already use (padding lat_min-1/lat_max+1 before slicing).

VERIFIED against real pulls (2026-08-03): the date/time/area string syntax below works. Three things an initial pull got wrong, now fixed: - Without explicit “direction”/”frequency” keys, MARS returns only a

single (directionNumber=1, frequencyNumber=1) bin per timestep, not the full spectrum – confirmed by decoding the returned GRIB directly via eccodes. “1/to/24”/”1/to/30” (1-based bin indices, matching this dataset’s fixed 24-direction/30-frequency discretization) requests every bin, turning one timestep into 24*30 GRIB messages.

  • The wave model’s native grid is a Reduced Lat-Lon grid, and MARS/MIR cannot crop that representation directly – “area” alone raised ReducedLL::croppedRepresentation() not supported. Requesting server-side regridding onto a regular grid via “grid” first (before cropping) is the standard fix and is what’s below.

  • “format” is deprecated in favor of “data_format” (CDS now warns but still honors “format”; use the current key name).

CrocoDash.raw_data_access.datasets.era5.decode_era5_spectra_grib(path)#

Reads a raw ERA5 2D wave spectra GRIB file (as downloaded by get_era5_2d_spectra) into a clean xr.Dataset with real dims (time, latitude, longitude, frequency, direction). See module docstring for why this can’t just be xr.open_dataset(path, engine=”cfgrib”).

CrocoDash.raw_data_access.datasets.gebco module#

class CrocoDash.raw_data_access.datasets.gebco.GEBCO#

Bases: BaseProduct

description = 'GEBCO (General Bathymetric Chart of the Ocean) is a public dataset of global ocean bathymetry'#
static get_gebco_data_script(output_folder=None, output_filename=None)#
static get_gebco_data_with_python(output_folder=None, output_filename=None)#
logger = <Logger GEBCO (INFO)>#
product_name = 'gebco'#

CrocoDash.raw_data_access.datasets.glofas module#

class CrocoDash.raw_data_access.datasets.glofas.GLOFAS#

Bases: DatedBaseProduct

description = '\tGLOFAS (Global Flood Awareness System) is a public river discharge/runoff Product'#
static get_global_data_with_python(dates, output_folder=PosixPath('.'), output_filename='glofas_data.nc')#

Downloads glofas data using cdsapi library. Note that users need to have an account with copernicus and have cdsapi installed and configured.

Parameters:
  • dates (list of str) – Two-element [start, end] range; every day in the range is requested.

  • output_folder (str, optional) – Directory where downloaded files will be saved.

  • output_filename (str, optional) – Filename to write inside output_folder.

static get_processed_global_glofas_script_for_cli(dates='UNUSED', output_folder=PosixPath('.'), output_filename='processed_glofas.nc')#

Downloads pre-processed global GloFAS discharge data from the CESM inputdata repository by generating a script users can run in their terminal.

Parameters:
  • dates (str, optional) – Currently unused; the published file is a fixed global dataset.

  • output_folder (str, optional) – Directory where downloaded files will be saved.

  • output_filename (str, optional) – filename in output directory

logger = <Logger GLOFAS (INFO)>#
product_name = 'glofas'#

CrocoDash.raw_data_access.datasets.glorys module#

Data Access Module -> Glorys

class CrocoDash.raw_data_access.datasets.glorys.GLORYS#

Bases: MOM6ForcingProduct

boundary_fill_method = 'regional_mom6'#
calendar = Calendar(cf='standard', cesm='GREGORIAN', mom6='gregorian')#
depth_coord = 'depth'#
description = 'GLORYS (Global Ocean Physics Reanalysis) is a public dataset provided through the copernicus marine service.'#
eta_var_name = 'zos'#
static get_glorys_data_from_cds_api(dates, lat_min, lat_max, lon_min, lon_max, name=None, output_folder=None, output_filename=None, variables=['zos', 'uo', 'vo', 'so', 'thetao'])#

Using the copernucismarine api, query GLORYS data (any dates)

static get_glorys_data_from_rda(dates: list, lat_min, lat_max, lon_min, lon_max, name=None, output_folder=PosixPath('.'), output_filename='raw_glorys.nc', variables=['time', 'latitude', 'longitude', 'depth', 'zos', 'uo', 'vo', 'so', 'thetao'], buf=1.0) → Dataset#

Gather GLORYS Data on Derecho Computers from the campaign storage and return the dataset sliced to the llc and urc coordinates at the specific dates

static get_glorys_data_script_for_cli(dates: tuple, lat_min, lat_max, lon_min, lon_max, output_folder, output_filename, variables=None, name=None, **kwargs) → None#

Script to run the GLORYS data query for the CLI

logger = <Logger GLORYS (INFO)>#
product_name = 'glorys'#
time_units = 'days'#
time_var_name = 'time'#
tracer_lat_coord = 'latitude'#
tracer_lon_coord = 'longitude'#
tracer_var_names = {'salt': 'so', 'temp': 'thetao'}#
tracer_x_coord = 'longitude'#
tracer_y_coord = 'latitude'#
u_lat_coord = 'latitude'#
u_lon_coord = 'longitude'#
u_var_name = 'uo'#
u_x_coord = 'longitude'#
u_y_coord = 'latitude'#
v_lat_coord = 'latitude'#
v_lon_coord = 'longitude'#
v_var_name = 'vo'#
v_x_coord = 'longitude'#
v_y_coord = 'latitude'#

CrocoDash.raw_data_access.datasets.reference module#

Data Access Module -> Reference (fast, deterministic synthetic forcing)

Three products – REFERENCE_OCEAN (MOM6), REFERENCE_ICE (CICE), REFERENCE_WAVES (WW3) – that generate plausible-looking forcing data purely in memory via numpy, with no network access, credentials, or campaign-storage dependency. Each access method is a pure function of its own (dates, lat/lon bbox) arguments: same inputs always produce the same output, so these are safe to use in tests (real assertions, not just “did it not crash”) and in demo notebooks that should look identical on every run.

These are not meant to be physically accurate – just close enough in shape (a warm-at-equator thermocline, an ice edge that tapers with latitude, a JONSWAP-shaped wave spectrum) that the real MOM6/CICE/WW3 regrid pipelines have something sensible to chew on, fast.

class CrocoDash.raw_data_access.datasets.reference.REFERENCE_ICE#

Bases: CICEForcingProduct

calendar = Calendar(cf='standard', cesm='GREGORIAN', mom6='gregorian')#
depth_coord = None#
description = 'Fast, deterministic synthetic CICE forcing for testing and demos -- generates its own grid, no real CICE restart/grid file required. Writes a complete CICE restart (ncat=5, every field CICE reads), built from one real ice column spread over a synthetic ice edge, so CICE can start from it.'#
static get_reference_ice_data(dates: list, lat_min, lat_max, lon_min, lon_max, name=None, output_folder=PosixPath('.'), output_filename='reference_ice.nc', variables=None, resolution_deg=0.5)#
logger = <Logger REFERENCE_ICE (INFO)>#
product_name = 'reference_ice'#
time_units = None#
time_var_name = None#
tracer_var_names = {}#
tracer_x_coord = 'ni'#
tracer_y_coord = 'nj'#
u_var_name = 'uvel'#
u_x_coord = 'ni'#
u_y_coord = 'nj'#
v_var_name = 'vvel'#
v_x_coord = 'ni'#
v_y_coord = 'nj'#
class CrocoDash.raw_data_access.datasets.reference.REFERENCE_OCEAN#

Bases: MOM6ForcingProduct

boundary_fill_method = 'regional_mom6'#
calendar = Calendar(cf='standard', cesm='GREGORIAN', mom6='gregorian')#
depth_coord = 'depth'#
description = 'Fast, deterministic synthetic ocean IC/OBC data (temperature, salinity, SSH, currents) for testing and demos -- no network or campaign-storage access required.'#
eta_var_name = 'ssh'#
static get_reference_ocean_data(dates: list, lat_min, lat_max, lon_min, lon_max, name=None, output_folder=PosixPath('.'), output_filename='reference_ocean.nc', variables=None, resolution_deg=0.5)#
logger = <Logger REFERENCE_OCEAN (INFO)>#
marbl_var_names = {'ALK': 'ALK', 'ALK_ALT_CO2': 'ALK_ALT_CO2', 'DIC': 'DIC', 'DIC_ALT_CO2': 'DIC_ALT_CO2', 'DOC': 'DOC', 'DOCr': 'DOCr', 'DON': 'DON', 'DONr': 'DONr', 'DOP': 'DOP', 'DOPr': 'DOPr', 'Fe': 'Fe', 'Lig': 'Lig', 'NH4': 'NH4', 'NO3': 'NO3', 'O2': 'O2', 'PO4': 'PO4', 'SiO3': 'SiO3', 'coccoC': 'coccoC', 'coccoCaCO3': 'coccoCaCO3', 'coccoChl': 'coccoChl', 'coccoFe': 'coccoFe', 'coccoP': 'coccoP', 'diatC': 'diatC', 'diatChl': 'diatChl', 'diatFe': 'diatFe', 'diatP': 'diatP', 'diatSi': 'diatSi', 'diazC': 'diazC', 'diazChl': 'diazChl', 'diazFe': 'diazFe', 'diazP': 'diazP', 'mesozooC': 'mesozooC', 'microzooC': 'microzooC', 'spC': 'spC', 'spChl': 'spChl', 'spFe': 'spFe', 'spP': 'spP'}#
product_name = 'reference_ocean'#
time_units = 'days'#
time_var_name = 'time'#
tracer_lat_coord = 'latitude'#
tracer_lon_coord = 'longitude'#
tracer_var_names = {'salt': 'salt', 'temp': 'temp'}#
tracer_x_coord = 'longitude'#
tracer_y_coord = 'latitude'#
u_lat_coord = 'latitude'#
u_lon_coord = 'longitude'#
u_var_name = 'u'#
u_x_coord = 'longitude'#
u_y_coord = 'latitude'#
v_lat_coord = 'latitude'#
v_lon_coord = 'longitude'#
v_var_name = 'v'#
v_x_coord = 'longitude'#
v_y_coord = 'latitude'#
class CrocoDash.raw_data_access.datasets.reference.REFERENCE_WAVES#

Bases: WW3ForcingProduct

calendar = Calendar(cf='standard', cesm='GREGORIAN', mom6='gregorian')#
description = 'Fast, deterministic synthetic WW3 boundary wave spectra (JONSWAP-shaped frequency spectrum, cosine-2s directional spreading) for testing and demos -- no CDS/network access required.'#
direction_convention = 'coming_from'#
static get_reference_wave_spectra(dates: list, lat_min, lat_max, lon_min, lon_max, name=None, output_folder=PosixPath('.'), output_filename='reference_waves.nc', variables=None)#
land_marker = 'zero'#
logger = <Logger REFERENCE_WAVES (INFO)>#
product_name = 'reference_waves'#
time_units = None#
time_var_name = 'time'#

CrocoDash.raw_data_access.datasets.seawifs module#

class CrocoDash.raw_data_access.datasets.seawifs.SeaWIFS#

Bases: BaseProduct

description = 'SEAWIFS is a Chlorophyll Dataset for MOM6'#
static get_global_seawifs_script_for_cli(output_folder=None, output_filename='UNUSED', username='')#

Downloads chlor_a data using NASA OceanData API with authentication.

Parameters:
  • username (str) – NASA Earthdata username (password will be prompted at runtime).

  • output_folder (str, optional) – Directory where downloaded files will be saved.

  • output_filename (str, optional) – Currently unused; the script names the downloaded file itself.

static get_processed_global_seawifs_script_for_cli(output_folder=PosixPath('.'), output_filename='processed_seawifs.nc')#

Downloads processed chlor_a data from the CESM inputdata repository by generating a script users can run in their terminal.

Parameters:
  • output_folder (str, optional) – Directory where downloaded files will be saved.

  • output_filename (str, optional) – filename in output directory

logger = <Logger SeaWIFS (INFO)>#
product_name = 'seawifs'#

CrocoDash.raw_data_access.datasets.srtm module#

class CrocoDash.raw_data_access.datasets.srtm.SRTM#

Bases: BaseProduct

description = 'SRTM15+ is a global 15 arc-second resolution bathymetry/topography dataset compiled from SRTM land topography and satellite-derived ocean bathymetry.'#
static get_srtm_data_script(output_folder=None, output_filename=None)#
static get_srtm_data_with_python(output_folder=None, output_filename=None)#
logger = <Logger SRTM (INFO)>#
product_name = 'srtm'#

CrocoDash.raw_data_access.datasets.utils module#

CrocoDash.raw_data_access.datasets.utils.convert_lons_to_180_range(*lons)#
CrocoDash.raw_data_access.datasets.utils.make_dates_end_inclusive(dates)#

Return (start, end) as “%Y-%m-%d %H:%M:%S” strings, with the end pushed to the last second of its day.

APIs that treat end_datetime as a literal cutoff (rather than a whole day) will otherwise silently drop everything after midnight on the last requested day.

CrocoDash.raw_data_access.datasets.utils.write_bash_curl_script(url, script_name, output_folder, output_filename)#

Module contents#

CrocoDash.raw_data_access.datasets.load_all_datasets()#

Dynamically import all modules in this package so that their _init_subclass hook run and populate the registry.