Plot Tide Range

This notebook demonstrates plotting the difference between the Highest Astronomical Tide (HAT) and Lowest Astronomical Tide (LAT) to get an estimate of the total tide range.

Important

Need to download tide model prior to running this notebook.

OTIS format tidal solutions provided by Oregon State University and ESR

Global Tide Model (GOT) solutions provided by Richard Ray at GSFC

Finite Element Solution (FES) provided by AVISO

Python Dependencies

Program Dependencies

  • io.model.py: retrieves tide model parameters for named tide models

  • io.OTIS.py: extract tidal harmonic constants from OTIS tide models

  • io.ATLAS.py: extract tidal harmonic constants from ATLAS netcdf models

  • io.GOT.py: extract tidal harmonic constants from GOT tide models

  • io.FES.py: extract tidal harmonic constants from FES tide models

Note

This notebook uses Jupyter widgets to set parameters for calculating the tidal maps.

Load modules

import numpy as np
import xarray as xr
import matplotlib
import datetime

matplotlib.rcParams["axes.linewidth"] = 2.0
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import cartopy.crs as ccrs
import ipywidgets

# import tide programs
import pyTMD.io
import pyTMD.tools
import timescale

# autoreload
%load_ext autoreload
%autoreload 2

Set parameters for program

  • Model directory

  • Tide model

# available model list
model_list = sorted(pyTMD.io.model.ocean_elevation())
# display widgets for setting directory and model
TMDwidgets = pyTMD.tools.widgets()
TMDwidgets.model.options = model_list
TMDwidgets.model.value = "GOT4.10_nc"
# create dropdown with calculation method
TMDwidgets.method = ipywidgets.Dropdown(
    options=["Approximate", "Calculated"],
    value="Approximate",
    description="Method:",
    disabled=False,
    style=TMDwidgets.style,
)
# start of 20-year epoch
TMDwidgets.datepick.description = "Epoch:"
TMDwidgets.datepick.value = datetime.datetime(2020, 1, 1)
TMDwidgets.VBox(
    [
        TMDwidgets.directory,
        TMDwidgets.model,
        TMDwidgets.method,
        TMDwidgets.datepick,
    ]
)

Read tide model

# get model parameters
model = pyTMD.io.model(
    TMDwidgets.directory.value,
).from_database(TMDwidgets.model.value)
# open dataset and convert to centimeters
ds = model.open_dataset(group="z", chunks="auto")
ds = ds.tmd.to_units("cm")

Setup coordinates for calculating tides

# create a global image
xlimits = [-180, 180]
ylimits = [-90, 90]
spacing = [1.0, 1.0]
# x and y coordinates
x = np.arange(xlimits[0], xlimits[1] + spacing[0], spacing[0])
y = np.arange(ylimits[0], ylimits[1] + spacing[1], spacing[1])
xgrid, ygrid = np.meshgrid(x, y)
# create xarray DataArrays for coordinates in crs of model
X, Y = ds.tmd.coords_as(xgrid, ygrid, type="grid", crs=4326)

Calculate tide range map

def get_peaks(ds, time=None, corrections=None):
    """
    Get the peak-to-peak range of a tidal time series
    """
    # calculate tides for date range
    tpred = ds.tmd.predict(time, corrections=corrections)
    # infer minor constituents and add to prediction
    tpred += ds.tmd.infer(time, corrections=corrections)
    # calculate tidal range as difference between astronomical high and low
    peak_to_peak = tpred.max(dim="time") - tpred.min(dim="time")
    return peak_to_peak
# interpolate model to grid points
ds1 = ds.tmd.interp(X, Y, extrapolate=False)
# model constituents
cons = ds1.tmd.constituents
# calculate peak-to-peak tide range
if TMDwidgets.method.value == "Approximate":
    # calculate worst case tide range (constructive interference of all)
    darr = ds1.tmd.to_dataarray()
    # use the worst case as an approximation of the peak-to-peak range
    worst_case = np.abs(darr).sum(dim="constituent", skipna=False)
    peak_to_peak = worst_case.compute()
else:
    # rechunk to smaller chunks for parallel computation
    ds1 = ds1.chunk({"x": 5, "y": 5})
    # create template for the output
    template = ds1[cons[0]]
    # use a time span greater than 18.6 year nodal cycle
    ts = timescale.from_datetime(TMDwidgets.datepick.value)
    # 20-year time span at hourly increments
    hours = np.arange(24 * 365.25 * 20)
    time = ts.tide + hours / 24.0
    # calculate peak-to-peak tide range
    peak_to_peak = xr.map_blocks(
        get_peaks,
        ds1,
        kwargs=dict(time=time, corrections=model.corrections),
        template=template,
    ).compute()

Create plots of tidal range

# cartopy transform for Equirectangular Projection
projection = ccrs.PlateCarree()
# create figure axis
fig, ax = plt.subplots(
    num=1, figsize=(5.5, 3.5), subplot_kw=dict(projection=projection)
)
# plot peak-to-peak tide range
sc = peak_to_peak.plot(
    ax=ax,
    vmin=0,
    add_colorbar=False,
    add_labels=False,
    transform=projection,
)
# add moderate resolution cartopy coastlines
ax.coastlines("50m")

# Add colorbar and adjust size
# pad = distance from main plot axis
# extend = add extension triangles to upper and lower bounds
# options: neither, both, min, max
# shrink = percent size of colorbar
# aspect = lengthXwidth aspect of colorbar
cbar = plt.colorbar(
    sc,
    ax=ax,
    extend="max",
    extendfrac=0.0375,
    orientation="horizontal",
    pad=0.025,
    shrink=0.90,
    aspect=22,
    drawedges=False,
)
# rasterized colorbar to remove lines
cbar.solids.set_rasterized(True)
# Add label to the colorbar
longname = f"{model.name} Tide Range"
cbar.ax.set_title(longname, fontsize=13, rotation=0, y=-2.0, va="top")
cbar.ax.set_xlabel("cm", fontsize=13, rotation=0, va="center")
cbar.ax.xaxis.set_label_coords(1.075, 0.5)
# ticks lines all the way across
cbar.ax.tick_params(
    which="both", width=1, length=16, labelsize=13, direction="in"
)

# axis = equal
ax.set_aspect("equal", adjustable="box")
# set x and y limits
ax.set_xlim(xlimits)
ax.set_ylim(ylimits)

# no ticks on the x and y axes
ax.get_xaxis().set_ticks([])
ax.get_yaxis().set_ticks([])
# stronger linewidth on frame
ax.spines["geo"].set_linewidth(2.0)
ax.spines["geo"].set_capstyle("projecting")

# adjust subplot within figure
fig.subplots_adjust(left=0.02, right=0.98, bottom=0.05, top=0.98)
# show the plot
plt.show()
../_images/0bb4f841c9c1e80a5c6661f298e2f239b35aab7379f507c02b2dabb6a460bc4b.png

Create plots of range classifications

# tide classifications
boundary = np.array([0.0, 200.0, 400.0, 2000.0])
ticklabels = ["Microtidal", "Mesotidal", "Macrotidal"]
longname = "Range Classification"
# calculate ticks for labels
ticks = 0.5 * (boundary[1:] + boundary[:-1])

# create figure axis
fig, ax = plt.subplots(
    num=2, figsize=(5.5, 3.5), subplot_kw=dict(projection=projection)
)
# create boundary norm
norm = colors.BoundaryNorm(boundary, ncolors=256)
# plot tide range classification
extent = (xlimits[0], xlimits[1], ylimits[0], ylimits[1])
sc = peak_to_peak.plot(
    ax=ax,
    add_colorbar=False,
    add_labels=False,
    norm=norm,
    cmap="plasma",
    transform=projection,
)
# add moderate resolution cartopy coastlines
ax.coastlines("50m")

# Add colorbar and adjust size
# pad = distance from main plot axis
# extend = add extension triangles to upper and lower bounds
# options: neither, both, min, max
# shrink = percent size of colorbar
# aspect = lengthXwidth aspect of colorbar
cbar = plt.colorbar(
    sc,
    ax=ax,
    extend="neither",
    extendfrac=0.0375,
    orientation="horizontal",
    pad=0.025,
    shrink=0.90,
    aspect=22,
    drawedges=False,
)
# rasterized colorbar to remove lines
cbar.solids.set_rasterized(True)
# Add label to the colorbar
cbar.ax.set_title(longname, fontsize=13, rotation=0, y=-2.0, va="top")
# Set the tick levels for the colorbar
cbar.set_ticks(ticks=ticks, labels=ticklabels)
cbar.ax.tick_params(which="both", length=0, labelsize=13)

# axis = equal
ax.set_aspect("equal", adjustable="box")
# set x and y limits
ax.set_xlim(xlimits)
ax.set_ylim(ylimits)

# no ticks on the x and y axes
ax.get_xaxis().set_ticks([])
ax.get_yaxis().set_ticks([])
# stronger linewidth on frame
ax.spines["geo"].set_linewidth(2.0)
ax.spines["geo"].set_capstyle("projecting")

# adjust subplot within figure
fig.subplots_adjust(left=0.02, right=0.98, bottom=0.05, top=0.98)
# show the plot
plt.show()
../_images/7ff0c793f506e3326e2db075924a5de2fd43f2d10672746150959de974c42a20.png