User Guide#
This guide covers the practical aspects of using PyFES for tidal prediction: configuration, runtime settings, prediction functions, and harmonic analysis. For installation and a minimal example, see Getting Started. For the mathematical foundations, see Theoretical Foundations.
Configuration#
PyFES loads tidal models from YAML configuration files using
pyfes.config.load(). The configuration describes the grid type, the
constituent files, and the prediction engine.
YAML File Structure#
A configuration file has the following structure:
engine: darwin # or 'perth'
tide:
cartesian:
paths:
M2: /path/to/M2_tide.nc
S2: /path/to/S2_tide.nc
K1: /path/to/K1_tide.nc
# ...
radial: # optional
cartesian:
paths:
M2: /path/to/M2_radial.nc
# ...
Top-Level Keys#
engineSelects the prediction engine:
darwin(FES/Darwin, default) orperth(PERTH/Doodson). See Prediction Engines for a comparison.tideThe ocean tide elevation model.
radialOptional radial (loading) tide model: the vertical deformation of the sea floor under the load of the ocean tide. It is evaluated with a second call to
evaluate_tide()onconfig.models['radial'], and the long-period equilibrium tide is never added to it. The geocentric tide measured by satellite altimetry is the sumtide + lp + load.
Each model section contains either a cartesian or lgp block describing
the grid type.
Cartesian Grids#
For regular latitude/longitude grids, each constituent is stored in a separate NetCDF file:
tide:
cartesian:
latitude: lat # variable name (default: 'lat')
longitude: lon # variable name (default: 'lon')
amplitude: amplitude # variable name (default: 'amplitude')
phase: phase # variable name (default: 'phase')
paths:
M2: /path/to/M2.nc
S2: /path/to/S2.nc
Note
During the loading process, PyFES checks that all constituents have
compatible grid properties (shape, resolution, longitude/latitude
ordering). If discrepancies are found, a warning is issued and the grid
will be resampled to match the first loaded constituent. See
resample() for details.
LGP Unstructured Meshes#
For finite-element meshes (LGP1 or LGP2 discretization), all constituents are stored in a single NetCDF file with variable name patterns:
tide:
lgp:
type: lgp2 # 'lgp1' (default) or 'lgp2'
path: /path/to/model.nc
latitude: lat # default: 'lat'
longitude: lon # default: 'lon'
triangle: triangle # triangle -> vertex connectivity
codes: codes # triangle -> LGP degree-of-freedom indices
amplitude: '{constituent}_amplitude'
phase: '{constituent}_phase'
max_distance: 0.0 # maximum extrapolation distance (0: none)
constituents:
- M2
- S2
- K1
Environment Variable Interpolation#
Paths can reference environment variables using ${VAR} syntax:
paths:
M2: ${FES_DATA}/M2_tide.nc
Dynamic Constituents#
The dynamic key lists constituents that are considered part of the atlas
but not stored as grid files. These are excluded from the long-period
equilibrium calculation and admittance inference:
tide:
cartesian:
dynamic:
- A5
paths:
M2: /path/to/M2.nc
Loading a Configuration#
import pyfes
config = pyfes.config.load('ocean_tide.yaml')
# Access the models and settings
tide_model = config.models['tide']
settings = config.settings
# Optionally load only a regional subset
config = pyfes.config.load('ocean_tide.yaml', bbox=(-10, 40, 10, 60))
The returned Configuration contains:
models: dictionary mapping'tide'and optionally'radial'to tidal model objects.settings: runtime settings (FESSettingsorPerthSettings) appropriate for the selected engine.
Runtime Settings#
All prediction parameters are configured through the Settings
class using a builder pattern. The two engine-specific subclasses differ in
their default values, including the engine itself and the long-period
equilibrium tide:
Setting |
||
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
num_threads (default 0, i.e. all available cores) controls the
parallelism. time_tolerance (in seconds, default 0) lets PyFES reuse the
astronomical angles and nodal corrections for dates closer than this value,
which speeds up predictions on dense time series.
All settings can be overridden on either engine using the builder methods.
Each with_* method modifies the object and returns it, so the calls can
be chained:
import pyfes
# Start from engine defaults
settings = pyfes.FESSettings()
# Customise individual settings
settings = (
pyfes.FESSettings()
.with_inference_type(pyfes.SPLINE)
.with_astronomic_formulae(pyfes.Formulae.MEEUS)
.with_num_threads(4)
)
# Or with the PERTH engine
settings = (
pyfes.PerthSettings()
.with_inference_type(pyfes.LINEAR)
.with_group_modulations(True)
)
Inference Modes#
PyFES infers minor tidal constituents not present in the atlas from nearby major constituents using admittance relationships. The inference mode is engine-generic: all four modes work identically with both the Darwin and PERTH engines.
Mode |
Description |
Recommended for |
|---|---|---|
|
No inference. Only atlas constituents are used. |
Diagnostics |
|
Linear admittance interpolation between reference constituents. |
GOT atlases (GOT4.10, GOT5.5, GOT5.6) |
|
Spline interpolation through reference constituents, providing smoother admittance curves. |
FES atlases (FES2014, FES2022) |
|
Low-order Fourier series (Munk–Cartwright approach) fitted to reference admittances. |
Research / comparison |
settings = pyfes.FESSettings().with_inference_type(pyfes.SPLINE)
settings = pyfes.PerthSettings().with_inference_type(pyfes.LINEAR)
# Any mode can be used with any engine
settings = pyfes.PerthSettings().with_inference_type(pyfes.SPLINE)
For the mathematical details of each inference method, see Admittance and Minor Constituent Inference.
Astronomic Formulae#
PyFES supports four sets of polynomial expressions for computing the fundamental variables (the six time-varying astronomical angles that define tidal frequencies):
Formulae |
Epoch |
Description |
|---|---|---|
|
J1900.0 |
First-order Schureman polynomials. Fastest; sufficient for most predictions near the present epoch. |
|
J1900.0 |
Third-order Schureman extensions. Better accuracy over longer time spans. |
|
J2000.0 |
Jean Meeus (1998) expressions using Terrestrial Dynamical Time. High precision over several centuries. |
|
J2000.0 |
IERS 2010 conventions based on Simon et al. (1994) lunisolar fundamental arguments. Highest precision. |
settings = pyfes.FESSettings().with_astronomic_formulae(
pyfes.Formulae.MEEUS
)
For the derivation of each formula set, see Astronomical Angle Computation.
Tidal Model Types#
PyFES supports three grid discretisations, each available in double
(Complex128) and single (Complex64) precision:
Cartesian grids — regular latitude/longitude grids. Each constituent is stored in a separate NetCDF file containing amplitude and phase arrays.
LGP1 — piecewise-linear (P1) Lagrange finite elements on unstructured triangular meshes. All constituents are stored in a single NetCDF file.
LGP2 — piecewise-quadratic (P2) Lagrange finite elements on unstructured triangular meshes, providing higher accuracy than LGP1 on the same mesh.
The grid type is determined by the YAML configuration (cartesian or lgp
section).
Prediction Functions#
PyFES provides three prediction functions.
evaluate_tide — From a Tidal Atlas#
Predicts tides by interpolating constituent amplitudes and phases from a tidal model at the requested locations:
tide, lp, flags = pyfes.evaluate_tide(
config.models['tide'],
dates, # numpy datetime64 array
longitudes, # numpy float64 array (degrees)
latitudes, # numpy float64 array (degrees)
settings=config.settings,
)
Returns:
tide: sum of the short-period constituents (diurnal, semidiurnal, compound waves), in the units of the atlas (typically cm).NaNwhere no data is available.lp: long-period part of the tide: the long-period constituents of the atlas, modelled or inferred (e.g. \(M_m\), \(M_f\)), plus the long-period equilibrium tide whencompute_long_period_equilibriumis enabled (the default withFESSettings, not withPerthSettings). The equilibrium tide is never added for aradialmodel.flags: quality indicators. Positive = number of interpolation points used; negative = extrapolated; zero = undefined.
evaluate_tide_from_constituents — From Known Harmonics#
Predicts tides from a dictionary of known constituents (e.g., from harmonic analysis of tide gauge data):
tide, lp = pyfes.evaluate_tide_from_constituents(
constituents, # {'M2': (amplitude, phase), ...}
dates,
latitude=48.38,
settings=settings,
)
Phases are Greenwich phase lags in degrees. Amplitudes must be in
centimetres when the long-period equilibrium tide is enabled (the default
with FESSettings), because that term is computed in
centimetres. The constituent names must belong to the engine selected by
settings (the Darwin engine if settings is omitted).
evaluate_equilibrium_long_period — Equilibrium LPE#
Computes the long-period equilibrium tide from the Cartwright–Tayler–Edden spectral tables:
lpe = pyfes.evaluate_equilibrium_long_period(
dates,
latitudes,
constituents=['Mm', 'Mf'], # optional: exclude these
)
The constituents parameter lists long-period constituents already present
in your atlas that should be excluded to avoid double-counting.
Harmonic Analysis#
PyFES also supports the inverse problem: extracting tidal constituents from observed sea-level time series.
Selecting Resolvable Constituents#
The Rayleigh criterion determines which constituents can be resolved from a given record length:
wt = pyfes.darwin.WaveTable() # or pyfes.perth.WaveTable()
# Select constituents resolvable from a 30-day record (in seconds)
names = wt.select_waves_for_analysis(duration=30 * 24 * 3600)
# Restrict the wave table to these constituents: analysing every
# constituent of the table would make the system ill-conditioned.
wt = pyfes.darwin.WaveTable(names)
Computing Nodal Modulations#
Build the matrices of nodal amplitude factors (\(f\)) and phase corrections (\(V+u\)) needed for the design matrix:
# f, vu: arrays of shape (n_constituents, n_times)
f, vu = wt.compute_nodal_modulations(dates)
By default, the nodal corrections are computed with the
SCHUREMAN_ORDER_1 formulae and without group modulations; pass
formulae=pyfes.Formulae.IERS to match the defaults of the PERTH engine.
Solving the Least-Squares Problem#
The static method harmonic_analysis() solves
the observation equation using least squares:
# h: observed sea level at one location, shape (n_times,)
amplitudes = pyfes.WaveTableInterface.harmonic_analysis(h, f, vu)
The result is a vector of complex amplitudes \(H_k e^{i G_k}\), one per
constituent, in the order of the wave table ([wave.name for wave in
wt]), which is not necessarily the order of names. The mean sea level
is estimated but not returned. To analyse several locations, call
harmonic_analysis once per time series: f and vu can be
reused. For the mathematical formulation, see
Harmonic Analysis: From Observations to Constituents.