Brown University
North Pacific Gyre Oscillation Di Lorenzo Research Group · Brown University

Methods

How the NPGO index is computed

The definition of the index, the satellite data it is built from, the step-by-step recipe used for every monthly update, the EOF pattern itself, and a short script that reproduces it.

1 · Definition

Second EOF of Northeast Pacific sea surface height

The North Pacific Gyre Oscillation (NPGO) index is the principal component of the second EOF of monthly sea surface height anomalies (SSHa) over the Northeast Pacific, 180°–110°W and 25°–62°N. It is reported in standard-deviation units.

The spatial pattern (the EOF) was computed once, from a regional ocean model hindcast of 1950–2004 (Di Lorenzo et al. 2008). That pattern has been held fixed ever since. Every month, satellite altimetry SSHa is projected onto it to extend the index forward. Recomputing the EOF each month would shift the historical values that are cited in the literature, so it is never done.

The index therefore has three segments:

PeriodSource
Jan 1950 – Dec 2004Model hindcast principal component (Di Lorenzo et al. 2008)
Jan 2005 – Dec 2022Satellite SSHa projected onto the fixed pattern; frozen, never rewritten
Jan 2023 – presentSatellite SSHa projected onto the fixed pattern; recomputed at every monthly update

2 · Data sources

Copernicus Marine sea level products

Satellite sea level from the Copernicus Marine Service (CMEMS): gridded L4 multi-satellite products at 0.125°, variable sla (sea level anomaly).

StreamProductDatasetCadenceRole
Delayed time (DT)SEALEVEL_GLO_PHY_L4_MY_008_047cmems_obs-sl_glo_phy-ssh_my_allsat-l4-duacs-0.125deg_P1M-mmonthly, 1993–The backbone. Lags the present by 8–12 months.
Near real time (NRT)SEALEVEL_GLO_PHY_L4_NRT_008_046cmems_obs-sl_glo_phy-ssh_nrt_allsat-l4-duacs-0.125deg_P1DdailyThe most recent months. Provisional.

Both are free with a Copernicus Marine account at data.marine.copernicus.eu.

The NPGO EOF pattern is provided as NPGO_EOF.nc. It holds eof(lon, lat) on a 255 × 201 grid (about 0.25° × 0.22°, 174.7°–111.3°W, 27.1°–61.1°N) and an ocean mask.

3 · Procedure

The monthly update, step by step

  1. Read the DT monthly sla fields and the NRT daily fields over the domain.
  2. Monthly means. Average the NRT daily fields into calendar months. Where a grid point is missing on any day, the month is missing at that point.
  3. Splice. Use DT for every month it covers, and NRT only for months after the last DT month. When DT catches up, its values replace the NRT ones.
  4. Anomalies. Subtract the monthly climatology computed over 1993–2004.
  5. Regrid. Bilinearly interpolate the anomalies onto the EOF grid.
  6. Project. For each month, pc(t) = Σxy eof(x,y) · ssha(x,y,t), summing over the points that are valid in both the pattern and the data for the whole record.
  7. Normalize. Over the months where the new projection overlaps the published index, rescale the projection to the published index's standard deviation and mean.
  8. Append the months that extend beyond the published index. Values through December 2022 are frozen and are checked against the previous file before anything is written.
Provisional tail. The newest one or two months rest on the NRT stream and on a partial month of daily data. Expect them to change slightly at the next update.

4 · Code

Minimal Python reproduction

The script below reproduces the satellite-era update with copernicusmarine, xarray, numpy and pandas. It downloads only the Northeast Pacific box (a few hundred MB for the full DT record). Download npgo_update.py.

# npgo_update.py -- project CMEMS sea level anomalies onto the NPGO pattern
#
# Minimal reproduction of the satellite-era NPGO update described at
# https://www.npgo.org/method.html. Needs: copernicusmarine, xarray, numpy,
# pandas, and a free Copernicus Marine account (run `copernicusmarine login`
# once). Place NPGO_EOF.nc and NPGO.txt (both from npgo.org) alongside.
#
# Cite Di Lorenzo et al. (2008), GRL, doi:10.1029/2007GL032838.

import copernicusmarine, numpy as np, pandas as pd, xarray as xr

BOX = dict(minimum_longitude=-180, maximum_longitude=-110,
           minimum_latitude=25,    maximum_latitude=62)

# 1. download DT monthly (1993-) and NRT daily (recent) SLA over the box
copernicusmarine.subset(
    dataset_id="cmems_obs-sl_glo_phy-ssh_my_allsat-l4-duacs-0.125deg_P1M-m",
    variables=["sla"], output_filename="sla_dt.nc", **BOX)
copernicusmarine.subset(
    dataset_id="cmems_obs-sl_glo_phy-ssh_nrt_allsat-l4-duacs-0.125deg_P1D",
    variables=["sla"], output_filename="sla_nrt.nc", **BOX)

# 2. monthly means; 3. splice NRT after the last DT month
dt  = xr.open_dataset("sla_dt.nc")["sla"].resample(time="1MS").mean()
nrt = xr.open_dataset("sla_nrt.nc")["sla"].resample(time="1MS").mean(skipna=False)
sla = xr.concat([dt, nrt.sel(time=nrt.time > dt.time[-1])], dim="time")

# 4. anomalies relative to the 1993-2004 monthly climatology
clim = sla.sel(time=slice("1993", "2004")).groupby("time.month").mean()
ssha = sla.groupby("time.month") - clim

# 5. regrid onto the EOF grid; 6. project
eof = xr.open_dataset("NPGO_EOF.nc")
pattern = eof["eof"] * eof["mask"]
ssha_i = ssha.interp(longitude=eof.lon, latitude=eof.lat)          # bilinear
valid = np.isfinite(pattern) & np.isfinite(ssha_i).all("time")      # common points
pc = (ssha_i.where(valid) * pattern.where(valid)).sum(("lon", "lat"))

# 7. normalize to the published index over the overlap
ref = np.loadtxt("NPGO.txt", comments="#")                          # year month value
ref_s = pd.Series(ref[:, 2], index=pd.PeriodIndex(
    [pd.Period(year=int(y), month=int(m), freq="M") for y, m in ref[:, :2]]))
pc_s = pd.Series(pc.values, index=pd.PeriodIndex(pc.time.values, freq="M"))
ov = pc_s.index.intersection(ref_s.index)
npgo_sat = (pc_s - pc_s[ov].mean()) / pc_s[ov].std() * ref_s[ov].std() + ref_s[ov].mean()

# 8. append the months beyond the published index
npgo = pd.concat([ref_s, npgo_sat[npgo_sat.index > ref_s.index[-1]]])
print(npgo.tail(6))

Run on the delayed-time archive (Jan 1993 – Dec 2025), this script reproduces the published index with a correlation of 0.997 over 2005–2025 and agrees with the newest months to three decimals. Over 1993–2004 the correlation is 0.98, because the published values for those years come from the model hindcast rather than from satellite data. Small residual differences come from the operational pipeline's exact land mask, regridding and normalization window.

The same projection in MATLAB, given ssha(x,y,t) already on the EOF grid:

load NPGO_EOF npgo                         % npgo.eof, npgo.mask, npgo.lon, npgo.lat
mask = mean(ssha,3) .* npgo.mask;  mask(~isnan(mask)) = 1;
E  = reshape(ssha .* mask, [], size(ssha,3));  E  = E(~isnan(E(:,1)),:)';
E2 = reshape(npgo.eof .* mask, [], 1);         E2 = E2(~isnan(E2));
pc = E * E2;                               % then normalize to NPGO.txt over the overlap

5 · Models & projections

Applying the method to model output or future years

The NPGO is an observational diagnostic, not a forecast. The recipe above, however, works on any gridded SSH field, which is how the index is estimated in climate-model simulations and projections:

  • Use the model's dynamic sea level (CMIP variable zos) over the same domain.
  • Remove the model's own monthly climatology and the domain-mean trend, so that global sea level rise does not project onto the pattern.
  • Project onto the observed pattern and normalize to the model's own historical period, or recompute the EOF within the model and confirm that its second mode resembles the observed one before using it.
  • Furtado et al. (2011) is the reference example of this analysis across a model ensemble.

Seasonal-to-interannual predictability of the NPGO comes mostly through the atmosphere: the index is the oceanic expression of the North Pacific Oscillation and is coupled to central Pacific ENSO (Di Lorenzo et al. 2010; Joh and Di Lorenzo 2017). Di Lorenzo et al. (2023) reviews the mechanisms.

6 · References

Papers behind the method

  • Di Lorenzo, E., et al., 2008: North Pacific Gyre Oscillation links ocean climate and ecosystem change. Geophys. Res. Lett., 35, L08607, doi:10.1029/2007GL032838. Cite this paper when using the index.
  • Di Lorenzo, E., et al., 2010: Central Pacific El Niño and decadal climate change in the North Pacific Ocean. Nature Geosci., 3, 762–765, doi:10.1038/ngeo984.
  • Furtado, J. C., E. Di Lorenzo, N. Schneider and N. A. Bond, 2011: North Pacific decadal variability and climate change in the IPCC AR4 models. J. Climate, 24, 3049–3067, doi:10.1175/2010JCLI3584.1.
  • Joh, Y., and E. Di Lorenzo, 2017: Increasing coupling between NPGO and PDO leads to prolonged marine heatwaves in the Northeast Pacific. Geophys. Res. Lett., 44, 11663–11671, doi:10.1002/2017GL075930.
  • Tranchant, B., I. Pujol, E. Di Lorenzo and J. F. Legeais, 2019: The North Pacific Gyre Oscillation. J. Oper. Oceanogr., 12 (sup1), Copernicus Marine Service Ocean State Report.
  • Di Lorenzo, E., et al., 2023: Modes and mechanisms of Pacific decadal-scale variability. Annu. Rev. Mar. Sci., 15, 249–275, doi:10.1146/annurev-marine-040422-084555.

Last revised September 2026. Questions about the method: e.dilorenzo@brown.edu.