Just one of the notebooks, aka what I did in classes :)

Data used in the code section below can be downloaded here.

Reading netCDF files

import math
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import warnings
import pandas as pd
import netCDF4 as nc
from netCDF4 import Dataset
# open a netcdf file
file='sst.mnmean.nc'
fh = Dataset(file, 'r') # Dataset is the class behavior to open the netCDF file
 
# fh means the file handle of the open netCDF file
# print fh format
print(fh.file_format)
 
# # print info about dimensions
print(fh.dimensions.keys())
print(fh.dimensions['time'])
 
# print info about variables
print(fh.variables.keys())
 
# print attributes
print(fh.Conventions)
 
for attr in fh.ncattrs():
    print(attr, '=',getattr(fh,attr))
    
# Extract fh from NetCDF file
lats = fh.variables['lat'][:] # extract/copy the fh
lons = fh.variables['lon'][:]
time = fh.variables['time'][:]
d_times=nc.num2date(fh.variables['time'][:],fh.variables['time'].units)
sst = fh.variables['sst'][:] # shape is time, lat, lon as shown above
sst_units=fh.variables['sst'].units
 
d_times=nc.num2date(fh.variables['time'][:],fh.variables['time'].units)
 
# Calulate NINO index, & plot the results
latidx_nino3 = (lats >=-5. ) & (lats <=5. )
lonidx_nino3 = (lons >=210. ) & (lons <=270. )
latidx_nino34 = (lats >=-5. ) & (lats <=5. )
lonidx_nino34 = (lons >=190. ) & (lons <=240. )
sst_3 = sst [:, latidx_nino3][..., lonidx_nino3]
sst_34 = sst [:, latidx_nino34][..., lonidx_nino34]
NETCDF3_CLASSIC
dict_keys(['lat', 'lon', 'time', 'nbnds'])
<class 'netCDF4._netCDF4.Dimension'> (unlimited): name = 'time', size = 494
dict_keys(['lat', 'lon', 'sst', 'time', 'time_bnds'])
CF-1.0
title = NOAA Optimum Interpolation (OI) SST V2
Conventions = CF-1.0
history = Wed Apr  6 13:47:45 2005: ncks -d time,0,278 SAVEs/sst.mnmean.nc sst.mnmean.nc
Created 10/2002 by RHS
comments = Data described in  Reynolds, R.W., N.A. Rayner, T.M.
Smith, D.C. Stokes, and W. Wang, 2002: An Improved In Situ and Satellite
SST Analysis for Climate, J. Climate
platform = Model
source = NCEP Climate Modeling Branch
institution = National Centers for Environmental Prediction
References = https://www.psl.noaa.gov/data/gridded/data.noaa.oisst.v2.html
dataset_title = NOAA Optimum Interpolation (OI) SST V2
source_url = http://www.emc.ncep.noaa.gov/research/cmb/sst_analysis/

Some further analysis and generated a plot to visualize the Sea Surface Temperature Anomalies (SSTA) over the Nino3 and Nino3.4 regions:

from datetime import datetime
times = []
for i in d_times:
    d = str(i.year) + '/' + str(i.month) + '/' + str(i.day)
    i = datetime.strptime(d,'%Y/%m/%d')
    # print(i)
    times.append(i)
#to get the mean values over lon/lat axis
nino3= np.mean(sst_3,axis=(1,2))-np.mean(sst_3)
nino34= np.mean(sst_34,axis=(1,2))-np.mean(sst_34)
plt.figure(1,figsize=(12,6))
plt.plot(times,nino3, color = "goldenrod", linewidth = 2, label = "Nin$o 3")
plt.plot(times,nino34, color = "maroon", linewidth = 3, label = "Nino 3.4")
plt.xlabel('Year')
plt.ylabel('Sea Surface Temperature Anomaly (oC)')
plt.legend(loc='upper left', prop = {'size':10})
plt.title('SSTA over Nino3 and Nino3.4')

Nino 3 Region, which sits between 5°S and 5°N latitude and 210° to 270° longitude, is in the central and eastern equatorial Pacific. Temperature changes linked to ENSO show up strongly here: intense warming when El Niño kicks in, significant cooling during La Niña. It’s right in the thick of the large-scale ocean-atmosphere interactions that drive ENSO’s strongest effects.

Nino 3.4 Region, which covers 5°S to 5°N latitude and 190° to 240° longitude, sits slightly farther east but still overlaps with the central Pacific. It straddles the boundary where warm waters from El Niño start to fade and where the eastern Pacific’s cooler upwelling processes begin.

The big reason Nino 3 experiences more extreme fluctuations than Nino 3.4 is because Nino 3 sits in a region where ENSO-related heat exchanges between the ocean and atmosphere are stronger, creating higher temperature anomalies. Nino 3.4, positioned closer to the eastern Pacific, has a shallower thermocline, meaning warm surface waters don’t mix as deeply with the colder layers below.

Plotting average SST over the oceans

sst_avg = np.mean(sst,axis=0)
 
# land-sea mask 
mask = Dataset('lsmask.nc', 'r').variables['mask'][:]
mask = mask.astype(float)
mask[mask == 0] = np.nan
#Let mask be the land-sea mask, we need to let the zero values be np.nan .
 
lon_2d, lat_2d = np.meshgrid(lons,lats)
fig = plt.figure(figsize=(20,8))
# Create a contour plot
plt.contourf(lon_2d, lat_2d, sst_avg*mask[0],levels=24,linewidths = 1, vmin = sst_avg.min(),vmax=sst_avg.max(), cmap = 'RdYlBu_r')
 
# Add colorbar and labels
plt.colorbar(label='Sea Surface Temperature (degC)')
plt.xlabel('Longitude')
plt.ylabel('Latitude')
plt.title('Average Sea Surface Temperature',fontsize=24)
plt.gca().set_facecolor("silver")
plt.savefig('08_average_SST.png',dpi=300)
plt.show()

In the tropics, especially in places like the equatorial Pacific, the Sun’s rays hit more directly and the energy arrives concentrated, so the ocean absorbs more heat and SSTs stay warm year-round. But up near the poles, the Sun’s rays strike at a lower angle, spreading out that energy over a larger area. That, combined with long, dark winters, results in much lower radiative heating, hence, colder ocean temperatures.

Plotting SST departures

I performed the following operations to calculate and visualize the departure of local Sea Surface Temperature (SST) from the zonal average field.

# Compute the zonal average SST
sst_zonal_avg = np.mean(sst, axis=2)
 
# Expand the zonal average array along the longitude axis to match the original SST shape
sst_zonal_avg_expanded = np.expand_dims(sst_zonal_avg, axis=2)
sst_zonal_avg_expanded = np.repeat(sst_zonal_avg_expanded, 360, axis=2)
 
# Calculate the departure of the local SST from the zonal average field
sst_departure = sst - sst_zonal_avg_expanded
 
# Calculate the mean of departure
sst_departure_avg = np.mean(sst_departure,axis=0)
 
cm = plt.cm.get_cmap('RdYlBu_r')
fig = plt.figure(figsize=(20,8))
# Create a contour plot
plt.contourf(lon_2d, lat_2d, (sst_departure_avg)*mask[0],levels=24,linewidths = 1,vmin=sst_departure_avg.min()+3,vmax=sst_departure_avg.max()-1, cmap = cm)
 
# Add colorbar and labels
plt.colorbar(label='Departure of local SST from zonal average field (degC)')
plt.xlabel('Longitude')
plt.ylabel('Latitude')
plt.title('Sea Surface Temperature Departure',fontsize=24)
plt.gca().set_facecolor("darkgrey")
plt.savefig('08_SST_departure.png',dpi=300)
plt.show()

SST departure is just how much the local sea surface temperature (SST) differs from the average across a given latitude. The deviations come from atmospheric and oceanic circulations moving heat around.

In the subtropics, the eastern parts of ocean basins tend to be cooler. Air flowing along the eastern edges of high-pressure systems (anticyclones) moves toward the equator and brings cooler temperatures with it, which is especially noticeable in the eastern Pacific. The western edges of those same systems push warm, moist air toward the poles, giving warmer SST departures in the western Pacific.

Up in the higher latitudes, things get chillier due to sub-polar cyclones. These weather systems create a poleward flow of cool air, reinforcing lower SST departures in these regions.

Along the equator there’s wind-driven upwelling. The trade winds sweep warm surface waters away from the equator, allowing deeper, colder, nutrient-rich water to rise up and replace it. This is why the equatorial eastern Pacific and Atlantic tend to have lower SST departures.