AMOCatlas demo

The purpose of this notebook is to demonstrate the functionality of AMOCatlas.

The demo is organised to show

  • Step 1: Loading and plotting a sample dataset

  • Step 2: Exploring the dataset attributes and variables.

Note that when you submit a pull request, you should clear all outputs from your python notebook for a cleaner merge.

[ ]:
import pathlib
import sys
import os
from amocatlas import read, plotters

script_dir = pathlib.Path().parent.absolute()
parent_dir = script_dir.parents[0]
sys.path.append(str(parent_dir))

# Specify the path for writing datafiles
data_path = os.path.join(parent_dir, "data")

Load RAPID 26°N

To see what files are available for each data source, use read.DATASOURCE.list_files(). So for the RAPID array, you can use read.rapid.list_files().

You can then use this list (or a subset thereof) to specify which files to be loaded and standardised using the read.rapid(file_list=["file1", "file2"]) function with the input file_list. It will return a list of xarray datasets, in the same order and the same length as file_list. An exception is if file_list has length = 1, in which case it will return just the xarray dataset.

[ ]:
# Find available files
rapid_file_list = read.rapid.list_files()

print("Available RAPID files:")
print(rapid_file_list)

# To specify to read two of those files, provide a file_list
standardRAPID = read.rapid(file_list=rapid_file_list[0:2])
[ ]:
# Plot RAPID timeseries
plotters.plot_amoc_timeseries(
    data=[standardRAPID[0]],
    varnames=["MOC"],
    labels=[""],
    resample_monthly=True,
    plot_raw=True,
    figsize=(10, 5),
    title="RAPID 26°N",
)

Load MOVE 16°N

To load the specified default transport file, use the read.DATASOURCE() function with no inputs. So for the MOVE array, this is read.move(). It will provide a single xarray dataset. For each array, a default transport file has been specified.

Developer’s note: This is specified in ~/amocatlas/data_source/DATASOURCE.py.

[ ]:
ds_move = read.move()
[ ]:
# Plot MOVE timeseries
plotters.plot_amoc_timeseries(
    data=[ds_move],
    varnames=["MOC"],
    labels=[""],
    colors=["darkgreen"],
    resample_monthly=True,
    plot_raw=True,
    title="MOVE 16°N - NADW Transport",
)

Load OSNAP

For just the transport file, you can use either read.osnap() as above, or read.osnap(transport_only=True). Both return a single xarray dataset.

[ ]:
ds_osnap = read.osnap()
ds_osnap
[ ]:
# Plot OSNAP timeseries
plotters.plot_amoc_timeseries(
    data=[ds_osnap],
    varnames=["MOC_SIGMA0"],
    labels=[""],
    colors=["darkblue"],
    resample_monthly=True,
    plot_raw=True,
    title="OSNAP",
)

Load SAMBA 34.5°S

If you want to load all datasets available for a datasource, provide the option all_files=True. Note that the first time you do this, it will download the data for you. On subsequent runs (set up from the same location) it will use the previously downloaded data.

See the data reports in the docs: https://amoccommunity.github.io/AMOCatlas/ if you want to check how big a dataset is before downloading it.

[ ]:
standardSAMBA = read.samba(all_files=True)
[ ]:
# Plot SAMBA timeseries
plotters.plot_amoc_timeseries(
    data=[standardSAMBA[0], standardSAMBA[1]],
    varnames=["UPPER_TRANSPORT", "MOC"],
    labels=["Kersale et al. 2020", "Meinen et al. 2018"],
    colors=["grey", "blue"],
    title="SAMBA 34.5°S",
    time_limits=("2000-01-01", "2022-12-31"),
    ylim=(-25, 25),
    resample_monthly=True,
    plot_raw=False,  # Raw data is a little spiky
)

Load FW2015

For a formatted table showing the data including variables, dimensions, units etc, use the function plotters.show_variables(DATASET) where DATASET is an xarray dataset.

[ ]:
standardfw2015 = read.fw2015()
plotters.show_variables(standardfw2015)
[ ]:
# Plot timeseries
plotters.plot_amoc_timeseries(
    data=[standardfw2015],
    varnames=["MOC_PROXY"],
    labels=[""],
    colors=["darkblue"],
    resample_monthly=True,
    plot_raw=True,
    title="FW2015",
)

LOAD SANCHEZ-FRANKS 2021 26°N satellite reconstruction

[ ]:
# Read dataset
standardsf2021 = read.sf2021()

# Show variables
plotters.show_variables(standardsf2021)
[ ]:
# Plot timeseries
plotters.plot_amoc_timeseries(
    data=[standardsf2021],
    varnames=["MOC_PROXY"],
    labels=[""],
    colors=["darkblue"],
    resample_monthly=False,
    # plot_raw=True,
    title="SF2021",
)

LOAD MOCHA 26.5°N

Note that AMOCatlas renames variables and updates units according to defaults specified in the code. For the MOCHA dataset, for example, the meridional heat transport is denoted by “Q”, whereas for other datasets it is “MHT”. To simplify intercomparison, we rename heat transport variables to “MHT”. When you use the read.mocha() this variable remapping has already taken place.

You can check the remapping applied in the docs: https://amoccommunity.github.io/AMOCatlas/

Additionally, some units such as “W” for Watts are converted to PetaWatts “PW” during the loading and standardisation. If instead you want to load the raw data, you can use read.mocha(raw=True).

[ ]:
standardMOCHA = read.mocha()

plotters.show_variables(standardMOCHA)
[ ]:
rawMOCHA = read.mocha(raw=True)
plotters.show_variables(rawMOCHA)
[ ]:
rawMOCHA
[ ]:
# Plot timeseries
fig, ax = plotters.plot_amoc_timeseries(
    data=[standardMOCHA, standardMOCHA, standardMOCHA],
    varnames=["MHT", "MHT_OT", "MHT_GYRE"],
    labels=["Total", "Overturning", "Gyre"],
    colors=["red", "darkblue", "black"],
    resample_monthly=True,
    plot_raw=False,
    title="MOCHA",
)
ax.legend(loc="lower right")

LOAD 41°N

Besides array-based datasets, some additional data sources are integrated within AMOCatlas. For instance, the Willis and Hobbs estimates of heat transport at 41°N and the Willis transport estimates using Argo and altimetry are available as datasource “WH41N”.

[ ]:
file_list = read.wh41n.list_files()
print("Available WH41N files:")
print(file_list)

standard41n = read.wh41n()

plotters.plot_amoc_timeseries(
    data=[standard41n],
    varnames=["MOC"],
    labels=[""],
    resample_monthly=True,
    plot_raw=False,
    colors=["darkblue"],
    title="41N",
)

Load Denmark Strait overflow and Faroe Bank Channel overflow transports

Overflow transports for Denmark Strait and Faroe Bank Channel are also available.

Note that in the current version of AMOCatlas we have not standardised the sign of the transports. In this case, a stronger DSO is more negative, whereas a stronger FBC is more positive.

[ ]:
standardDSO = read.dso()
standardFBC = read.fbc()

plotters.plot_amoc_timeseries(
    data=[standardDSO, standardFBC],
    varnames=["TRANS_DSO", "TRANS_FBC"],
    labels=["DSO", "FBC"],
    resample_monthly=True,
    plot_raw=True,
    colors=["yellow", "orange"],
    title="DSO and FBC",
)

Load Calafat2025

Meridional heat transport from a Bayesian method to produce a North Atlantic heat budget is also available. However, it has 4000 realisations of the time series (from which uncertainties can be estimated), so is less straightforward to plot. See below for an average across the realisations.

[ ]:
standardCALAFAT2025 = read.calafat2025()
standardCALAFAT2025
[ ]:
def create_ensemble_mean_dataset(ds):
    """Create a new dataset with ensemble means, removing N_ENSEMBLE dimension.

    This function takes the mean across the N_ENSEMBLE dimension for all variables
    that have it, creating a dataset suitable for standard plotting functions.

    Parameters
    ----------
    ds : xarray.Dataset
        Input dataset with N_ENSEMBLE dimension

    Returns
    -------
    xarray.Dataset
        Dataset with ensemble means, N_ENSEMBLE dimension removed

    """
    # Create a copy of the dataset
    ds_mean = ds.copy()

    # For each data variable, take the mean across N_ENSEMBLE if it has that dimension
    for var_name in ds.data_vars:
        var = ds[var_name]
        if "N_ENSEMBLE" in var.dims:
            # Take mean across N_ENSEMBLE dimension (point estimate)
            ds_mean[var_name] = var.mean(dim="N_ENSEMBLE")

    # Remove N_ENSEMBLE coordinate since no variables use it anymore
    if "N_ENSEMBLE" in ds_mean.coords:
        ds_mean = ds_mean.drop_vars("N_ENSEMBLE")

    return ds_mean
[ ]:
# Create the ensemble-averaged dataset
calafat_mean = create_ensemble_mean_dataset(standardCALAFAT2025)

# Now your original plotting code will work:
lat_idx = 5
lat_val = calafat_mean["LATITUDE"].values[lat_idx]

if lat_val < 0:
    title_str = f"CALAFAT2025 (lat = {-lat_val:.2f}°S)"
else:
    title_str = f"CALAFAT2025 (lat = {lat_val:.2f}°N)"

# Plot the 2D MHT data
fig, ax = plotters.plot_amoc_2d_data(
    data=calafat_mean,
    varname="MHT",
    title="CALAFAT2025 Meridional Heat Transport",
    ylabel="Latitude (°N)",
    figsize=(12, 6),
    colormap="RdBu_r",  # Red-blue colormap, good for heat transport
    # vmin=-1.0,  # Optional: set color scale limits
    # vmax=1.0,
)

# Add colorbar label
cbar = fig.get_axes()[1] if len(fig.get_axes()) > 1 else None
if cbar:
    cbar.set_ylabel("MHT (PW)", rotation=270, labelpad=20)

Load Zheng2024

Freshwater transports also available.

[ ]:
# load ZHENG2024 dataset
standardZHENG2024 = read.zheng2024()

# create mean by using function defined for calafat2025 dataset earlier
zheng_mean = create_ensemble_mean_dataset(standardZHENG2024)

# Plot the 2D MFT data
fig, ax = plotters.plot_amoc_2d_data(
    data=zheng_mean,
    varname="MFT",
    title="ZHENG2024 Meridional Freshwater Transport",
    ylabel="Latitude (°N)",
    figsize=(12, 6),
    colormap="RdBu_r",  # Red-blue colormaps
    # vmin=-1.0,  # Optional: set color scale limits
    # vmax=1.0,
)

# Add colorbar label
cbar = fig.get_axes()[1] if len(fig.get_axes()) > 1 else None
if cbar:
    cbar.set_ylabel("MFT (Sv)", rotation=270, labelpad=20)

Load NAC timeseries

Available for plotting is a transport estimate by satellite and float observations (TRANS_NAC) and a proxy only based on satellite altimetry (TRANS_NAC_PROXY). Both are shown here.

Dataset is already in 6-monthly resolution, which is why the raw data is shown in the plot. If you type resample_monthly = False then the raw data is shown in your desired color.

[ ]:
# Load dataset
standardNAC = read.nac()
[ ]:
fig, ax = plotters.plot_amoc_timeseries(
    data=[standardNAC, standardNAC],
    varnames=["TRANS_NAC", "TRANS_NAC_PROXY"],
    labels=["Transport", "Proxy"],
    colors=["red", "darkblue"],
    resample_monthly=False,
    plot_raw=True,
    title="NAC - North Atlantic Current",
    ylim=(15, 35),
)
ax.legend(loc="lower right")  # Plot NAC transport and proxy timeseries

Load Le Bras 35°N AMOC

[ ]:
# Load dataset
# This dataset has multiple files, so we specify all_files=True create a list of datasets.
# They can then be accessed with standardLEBRAS35N[0] (transport data) and standardLEBRAS35N[1] (gridded velocities).
# If you only want the transport files, you could specify: transport_only=True
standardLEBRAS35N = read.lebras35n(all_files=True)
[ ]:
# Plot MOC and Ekman component of transport timeseries
# In the plot the data is displayed as (raw) because it is already on a monthly grid, so no resampling was needed.
plotters.plot_amoc_timeseries(
    data=[standardLEBRAS35N[0], standardLEBRAS35N[0]],
    varnames=["MOC_SIGMA2", "TRANS_EKMAN"],
    labels=["MOC", "Ekman"],
    resample_monthly=False,
    plot_raw=True,
    colors=["red", "darkblue"],
    title="Le Bras et al. 2023 - 35N",
)

Load the AXMOC transport data for 22.5°S and 34.5°S

  • use read.axmoc22s() for the 22.5°S data (here we have MOC and MHT available)

  • use read.axmoc34s() for the 34.5°S data (here we have MOC, MHT and FOV available) (FOV = overturning component of freshwater transport)

For both datasets we have the total, then the Ekman and the geostrophic component available

[ ]:
standardAXMOC22S = read.axmoc22s()
standardAXMOC34S = read.axmoc34s()
[ ]:
plotters.plot_amoc_timeseries(
    data=[standardAXMOC22S, standardAXMOC34S],
    varnames=["MOC", "MOC"],
    labels=["MOC 22.5°S", "MOC 34.5°S"],
    resample_monthly=False,
    plot_raw=True,
    colors=["red", "darkblue"],
    title="AXMOC - MOC transport at 22.5°S and 34.5°S",
)

Monthly Anomalies Overview

[ ]:
plotters.plot_monthly_anomalies(
    osnap_data=ds_osnap["MOC_SIGMA0"],
    fortyone_data=standard41n["MOC"],
    rapid_data=standardRAPID[0]["MOC"],
    move_data=-ds_move["MOC"],
    samba_data=standardSAMBA[1]["MOC"],
    fw2015_data=standardfw2015["MOC_PROXY"],
    dso_data=standardDSO["TRANS_DSO"],
    osnap_label="OSNAP",
    fortyone_label="41°N",
    rapid_label="RAPID 26°N",
    move_label="MOVE 16°N",
    samba_label="SAMBA 34.5°S",
    fw2015_label="FW2015",
    dso_label="DS Overflow Transport",
)

Other components

It is also possible to manipulate (filter) and plot other components of the AMOC, depending on what is available in the datasets.

[ ]:
clim = standardRAPID[0].groupby("TIME.month").mean("TIME")
tmp = standardRAPID[0].groupby("TIME.month") - clim
filtRAPID = tmp.rolling(TIME=500, center=True).mean()

fig, ax = plotters.plot_amoc_timeseries(
    data=[filtRAPID],
    varnames=["TRANS_3000_5000"],
    labels=[""],
    resample_monthly=True,
    plot_raw=True,
    title="RAPID 26°N - t_ld10",
)
ax.set_ylim(4, -3)

fig.show()