Tidal Analysis Guide

Overview

tidal-analysis extracts tidal harmonic constituents from model output and validates them against satellite tidal atlases (FES2014, TPXO9) or tide gauge records from the GESLA database.

The harmonic analysis uses utide to fit sinusoidal components at each grid point or station location. Model amplitudes and phases are then compared to the reference atlas or gauge data.

Analysis modes

ModeFlagDescription
gesla--mode geslaHarmonic analysis at GESLA tide gauge locations (fast; recommended; config default)
spatial--mode spatialHarmonic analysis on the full model grid, compared to a tidal atlas
both--mode bothRun spatial and GESLA together
stations--mode stationsNamed stations listed under stations: in config

Model source: --source / --experiment

The model file to read no longer lives in the per-run config. It comes from config/sources.yaml, keyed by --source, with --experiment naming the run folder under that source:

tidal-analysis --config config/tidal_analysis.yaml --source CMEMS --experiment tidal --years 2011 2011

--experiment is required whenever --source is given — it is the run folder name (e.g. tidal, run01), not the full output label. The CLI builds the output label itself as <SOURCE>/<experiment> (and <SOURCE>/<model>-<scenario>/<experiment> for CMIP6/CMIP6_raw, via --model/--scenario), so config['experiment'] in the printed summary is already the composite name.

Current sources for NSe: CMEMS, WOA, CMIP6 (bias-corrected forcing), CMIP6_raw (forcing without bias correction — e.g. the ssp126 run). CMIP6 vs CMIP6_raw is a real distinction, not a typo: use CMIP6_raw for any run whose atmospheric forcing was not bias-corrected.

Without --source, the config’s own model.base_path / model.filename_pattern are required instead (the pre-sources.yaml way); that path still works but is no longer how NSe runs are set up.

Config structure

Annotated example: config/tidal_analysis.yaml

# config/tidal_analysis.yaml
area: NSe
experiment: TPXO9        # overridden by --experiment when --source is used

years: [2020, 2022]      # loads and concatenates 2020, 2021, 2022
# For a single year use:  year: 2022

constituents:
  - M2
  - S2
  - K1
  - O1
  # ... N2, K2, P1, Q1

gesla:
  path: ${GESLA_FOLDER}/122848.csv
  amp_units: cm
  filters:
    type: Coastal
    min_years: 2
    coverage: grid
    grid_deg: 1.0

stations:                # named stations for --mode stations
  - name: Aberdeen
    lon: -2.08
    lat: 57.14

reference:                # spatial-mode atlas
  type: FES2014
  path: ${TIDAL_FOLDER}/FES2014/ocean_tide.nc

model:
  name: "pyGETM"
  # File location and the SSH variable come from config/sources.yaml when
  # --source is given; see the --source section above.

output:
  analyses_dir: "${OCEANICU_ANALYSES_FOLDER}"   # no default — see below
  plots: true
  statistics: true

analysis:
  mode: gesla

Required keys: area, experiment, and (only without --source) model.base_path, model.filename_pattern.

output.analyses_dir has no default

analyses_dir must resolve to a real path — there is no ./analyses fallback. It is expanded from OCEANICU_ANALYSES_FOLDER in this machine’s data-roots file (<hostname>_ocean-post_data_roots.yaml), or set directly with --analyses-dir. If neither is set, the run stops immediately with an error naming the missing variable, rather than writing output somewhere unexpected (e.g. inside the code checkout).

Usage

# CMEMS, --source form (current NSe workflow)
tidal-analysis --config config/tidal_analysis.yaml --source CMEMS --experiment tidal --years 2011 2011 --workers 16

# WOA
tidal-analysis --config config/tidal_analysis.yaml --source WOA --experiment tidal --years 2011 2011 --workers 16

# CMIP6 historical
tidal-analysis --config config/tidal_analysis.yaml --source CMIP6 --experiment tidal --years 2011 2011 --workers 16

# Spatial mode, single constituent
tidal-analysis --config config/tidal_analysis.yaml --source CMEMS --experiment tidal --mode spatial --constituents M2

# GESLA stations only (also the config default)
tidal-analysis --config config/tidal_analysis.yaml --source CMEMS --experiment tidal --mode gesla

# Append to an existing statistics file (useful when running multiple datasets)
tidal-analysis --config config/tidal_analysis.yaml --source CMEMS --experiment tidal \
    --dataset-name "FES2014" --append-stats

Output

<OCEANICU_ANALYSES_FOLDER>/areas/<AREA>/validations/<SOURCE>/<experiment>/
├── plots/
│   └── physics/
│       └── pyGETM/
│           └── <period>/        ← e.g. 2011
│               └── tidal/
│                   ├── <const>_comparison_<...>.png
│                   └── gesla_station_comparison_maps_{diurnal,semidiurnal}.png
└── tables/
    └── physics/
        └── pyGETM/
            └── tidal/
                ├── gesla_station_comparison.csv   ← one row per station × constituent
                └── tidal_validation_statistics.txt

For CMIP6/CMIP6_raw, the path is .../<SOURCE>/<model>-<scenario>/<experiment>/..., e.g. CMIP6_raw/GFDL-ESM4-ssp126/run01/.

Results are also written to the shared statistics database (analyses/statistics.db, table tidal_statistics) keyed on area + experiment (the composite <SOURCE>/<experiment> label), unless --no-staging is given.

Statistics file format

tidal_validation_statistics.txt has one section per constituent with area-averaged metrics:

M2 Amplitude
  RMSE:        0.032 m
  Bias:       -0.008 m
  Correlation: 0.987

M2 Phase
  RMSE:        4.2 deg
  Bias:        1.1 deg

When --append-stats is used, each call adds a new section labelled with --dataset-name, making it easy to compare multiple atlases or gauge datasets in one file.

Hugo reporting

Running regenerate_hugo.py --apply after a tidal run creates/updates the Hugo page at content/validations/<area>-<source>-<experiment>.md (with / and _ in the label flattened to -, e.g. nse-cmems-tidal.md), including the GESLA station comparison table. See the reporting guide and the Hugo deployment guide.

Performance

The spatial mode runs utide.solve() at every model grid point. For large domains this is the bottleneck. Use --workers N to parallelise across grid rows (default: all available CPUs). A typical North Sea grid (200×300 points) with 8 years of hourly output takes 5–15 minutes depending on the number of constituents.

The GESLA mode only analyses at gauge locations (~a few hundred) and is much faster.