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
| Mode | Flag | Description |
|---|---|---|
gesla | --mode gesla | Harmonic analysis at GESLA tide gauge locations (fast; recommended; config default) |
spatial | --mode spatial | Harmonic analysis on the full model grid, compared to a tidal atlas |
both | --mode both | Run spatial and GESLA together |
stations | --mode stations | Named 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.