Air Sea ExchangeΒΆ

import xarray as xr
from matplotlib import pyplot as plt
import numpy as np
import gsw
import pooch
import cartopy.crs as ccrs
import hvplot.xarray
import hvplot.pandas
import re

%matplotlib inline
%config InlineBackend.figure_format = 'retina'
plt.rcParams['figure.figsize'] = (15,9)
plt.rcParams['font.size'] = 16
#plt.rcParams['lines.linewidth'] = 3
from IPython.display import SVG, display, Image, display_svg, YouTubeVideo

opts = dict(geo=True, frame_width=800, frame_height=420, projection=ccrs.Robinson(), coastline=True, legend=False ,features={'land': '110m'})

As anyone who has been to sea knows, the interface between ocean and atmosphere can be an incredibly violent place. Within a hurricane, for example, it is in fact impossible to draw a clear boundary between ocean and atmosphere; the extreme winds whip up a continuous emulsion of air and water. Nevertheless, outside of such extreme cases, there exist robust and relatively simple equations for describing the exchange of heat, freshwater, and momentum between atmosphere and ocean.

display(YouTubeVideo("BhduxaGtNJ8", width=640, height=480))

Atmospheric CirculationΒΆ

Although this is an oceanography class, this topic requires us to know a bit about the atmosphere. We must have some grasp of the patterns of global atmospheric circulation in order to interpret the patterns of cloud, precipitation, and evaporation which influence air-sea interaction.

Hadley CirculationΒΆ

The large-scale atmospheric circulation is shown in this figure:

Kaidor, CC BY-SA 3.0 https://creativecommons.org/licenses/by-sa/3.0, via Wikimedia Commons

Major important feature of the global circulation include:

  • Two overturning ciruclation cells (Hadley Cells) centered around the equator:

    • Strong ascent near the equator (Intertropical Convergence Zone; ITCZ), which drives precipitation and cloud cover.

    • Strong descent in the extratropics. The descending air comes from high in the atmosphere and is thus very dry. As a result, there is little rain and atmospheric moisture at these latitudes. This is why we have deserts.

  • Strong westerly winds in midlatitudes

  • Strong synoptic-scale storms in midlatitudes. These storms bring heavy wind gusts and precipitation to higher latitudes.

The Mixed LayerΒΆ

Our starting point is to consider the ocean surface mixed layer and its coupling with the atmosphere. An important property of the surface ocean is that it nearly always contains a layer of well-mixed (i.e. vertically homogeneous) water overlying the stratified interior (the thermocline / pycnocline). This is illustrated in the ARGO profiles shown below.

import fsspec

url = 'https://www.ncei.noaa.gov/data/oceans/argo/gadr/data/atlantic/2015/09/nodc_D1901358_079.nc'
with fsspec.open(url) as fp:
    argo = xr.open_dataset(fp).isel(n_prof=0).load()
argo['s_a'] = gsw.SA_from_SP(argo.psal, argo.pres, argo.longitude, argo.latitude)
argo['c_t'] = gsw.CT_from_t(argo.s_a, argo.temp, argo.pres)
argo['rho'] = gsw.rho(argo.s_a, argo.c_t, argo.pres)
argo['sig0'] = gsw.sigma0(argo.s_a, argo.c_t)
argo['sig2'] = gsw.sigma2(argo.s_a, argo.c_t)
argo['sig4'] = gsw.sigma4(argo.s_a, argo.c_t)
Nsquared, p_mid = gsw.Nsquared(argo.s_a, argo.c_t, argo.pres)

argo_p = argo.swap_dims({'n_levels': 'pres'})
argo_p
<xarray.Dataset>
Dimensions:                       (n_calib: 1, n_history: 4, n_param: 3, pres: 77)
Coordinates:
  * pres                          (pres) float32 6.2 10.3 ... 1.9e+03 1.95e+03
Dimensions without coordinates: n_calib, n_history, n_param
Data variables: (12/70)
    data_type                     object b'Argo profile    '
    format_version                object b'3.1 '
    handbook_version              object b'1.2 '
    reference_date_time           object b'19500101000000'
    date_creation                 object b'20161104225754'
    date_update                   object b'20170714162347'
    ...                            ...
    s_a                           (pres) float64 36.52 36.52 36.52 ... 35.1 35.1
    c_t                           (pres) float64 22.69 22.68 ... 3.299 3.239
    rho                           (pres) float64 1.025e+03 ... 1.037e+03
    sig0                          (pres) float64 25.03 25.04 ... 27.8 27.81
    sig2                          (pres) float64 33.39 33.39 ... 36.93 36.94
    sig4                          (pres) float64 41.39 41.39 ... 45.66 45.67
Attributes: (12/49)
    title:                           Argo float vertical profile
    institution:                     CORIOLIS
    source:                          Argo float
    history:                         2018-06-09T01:59:23Z csun convAGDAC.f90 ...
    references:                      http://www.nodc.noaa.gov/argo/
    user_manual_version:             3.1
    ...                              ...
    time_coverage_end:               2015-09-07T12:29:26Z
    time_coverage_duration:          point
    time_coverage_resolution:        point
    gadr_ConventionVersion:          GADR-3.0
    gadr_program:                    convAGDAC.f90
    gadr_programVersion:             1.0
fig, ax = plt.subplots(ncols=3, figsize=(15,6))
argo_p.temp.plot(y='pres', ax=ax[0], yincrease=False, label='T')
argo_p.c_t.plot(y='pres', ax=ax[0], yincrease=False, label=r'$\Theta$')
ax[0].set_xlabel('[deg. C]')
argo_p.psal.plot(y='pres', ax=ax[1], yincrease=False, label=r'$S_p$')
argo_p.s_a.plot(y='pres', ax=ax[1], yincrease=False, label=r'$S_A$')
ax[1].set_xlabel('[PSU / g/kg]')
argo_p.sig0.plot(y='pres', ax=ax[2], yincrease=False, label=r'$\sigma_0$')
ax[2].set_xlabel('[kg/m^3]')

[a.set_ylim([500,0]) for a in ax]
[a.set_ylabel(r'$p$ (dbar)') for a in ax]
[a.legend() for a in ax]
[a.grid() for a in ax]
fig.suptitle('ARGO Float ID %s : %g lon, %g lat' % (argo.id, argo.longitude, argo.latitude))
plt.tight_layout()
plt.close()
fig
_images/03_air_sea_exchange_11_0.png

The mixed layer exists primarily because of the winds; the winds generate surface waves which break, and the resulting turbulence homogenizes the properties of the near-surface water.

Generally speaking, the stronger the winds, the deeper the mixed layer. However, buoyancy fluxes can also help determine the mixed layer depth. In winter, strong cooling causes the surface water to become denser than the underlying water. This triggers turbulent convection which deepens the mixed layer.

url = 'http://mixedlayer.ucsd.edu/data/Argo_mixedlayers_monthlyclim_03172021.nc'
file_path = pooch.retrieve(url, known_hash=None)
mld = xr.open_dataset(file_path)
mld = mld.swap_dims({'iLON': 'lon', 'iLAT': 'lat', 'iMONTH': 'month'})
mld
<xarray.Dataset>
Dimensions:        (lat: 180, lon: 360, month: 12)
Coordinates:
  * lat            (lat) float64 -89.5 -88.5 -87.5 -86.5 ... 86.5 87.5 88.5 89.5
  * lon            (lon) float64 -179.5 -178.5 -177.5 ... 177.5 178.5 179.5
  * month          (month) float64 1.0 2.0 3.0 4.0 5.0 ... 9.0 10.0 11.0 12.0
Data variables: (12/17)
    mld_da_mean    (lat, lon, month) float64 ...
    mld_dt_mean    (lat, lon, month) float64 ...
    mld_da_median  (lat, lon, month) float64 ...
    mld_dt_median  (lat, lon, month) float64 ...
    mld_da_std     (lat, lon, month) float64 ...
    mld_dt_std     (lat, lon, month) float64 ...
    ...             ...
    mlt_dt         (lat, lon, month) float64 ...
    mls_da         (lat, lon, month) float64 ...
    mls_dt         (lat, lon, month) float64 ...
    num            (lat, lon, month) float64 ...
    longrid        (lat, lon) float64 -179.5 -178.5 -177.5 ... 177.5 178.5 179.5
    latgrid        (lat, lon) float64 -89.5 -89.5 -89.5 -89.5 ... 89.5 89.5 89.5
Attributes:
    description:  This file was created using \n matlab R2011b with NETCDF Li...
    date:         [730848.53015047 730848.88790506 730849.28543982 ... 738207...
mld.mld_da_max.hvplot('lon', 'lat', clim=(0, 500), label='Maximum Mixed Layer Depth', cmap='Viridis', **opts)
/srv/conda/envs/notebook/lib/python3.8/site-packages/cartopy/io/__init__.py:241: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/110m_physical/ne_110m_land.zip
  warnings.warn(f'Downloading: {url}', DownloadWarning)
/srv/conda/envs/notebook/lib/python3.8/site-packages/cartopy/io/__init__.py:241: DownloadWarning: Downloading: https://naturalearth.s3.amazonaws.com/110m_physical/ne_110m_coastline.zip
  warnings.warn(f'Downloading: {url}', DownloadWarning)

Simple 1D ModelΒΆ

The mixed layer is the part of the ocean that is in direct contact with the atmosphere, and so our equations for air-sea interaction are equations for the evolution of the mixed layer. For the sake of simplicity, we will begin by considering just a one-dimensional model, where all exchange is in the vertical. (In other words, we will not consider lateral exchanges of heat and salt within the mixed layer.) Furthermore, we will assume that the mixed layer is totally isolated from the thermocline and does not exchange heat and salt with the water below. Both these assumptions are unrealistic, but they are a useful starting point.

In this model, the mixed layer thermodynamic state is characterized by three fundamental state variables: \(\Theta\) (conservative temperature), \(S_A\) (absolute salinity), and \(h\), the mixed layer depth. Through the equation of state, \(\Theta\) and \(S_A\) determine the mixed layer potential density. (The pressure is irrelevant for now, but in this 1D model, it is simply proportional to depth.) We now describe how air-sea exchange influences the evolution of \(\Theta\) and \(S_A\).

Heat ExchangeΒΆ

The heat content of the mixed layer (per unit area) is given by.

\[\mathcal{H} = h \rho c_p^0 \Theta \]

If the mixed layer is isolated from the interior, the first law of thermodynamics can be written as

\[ \frac{d \mathcal{H}}{dt} = Q \]

where \(Q\) is the air-to-sea heat flux.

Because density \(\rho\) only changes by a about 0.1% during air-sea interaction, it is reasonable to approximate \(\rho \simeq \rho_0\). If we further assume that the mixed layer depth \(h\) doesn’t change, we can write the heat equation as

\[ \frac{d \Theta}{d t} = \frac{Q}{h \rho_0 c_p^0} \]

Components of the Heat FluxΒΆ

We divide the heat flux \(Q\) into four separate components associated with different physical processes.

\[ Q = \underbrace{Q_{sw}}_\text{shortwave radiation} + \underbrace{Q_{lw}}_\text{longwave radiation} + \underbrace{Q_{sh}}_\text{sensible heat flux} + \underbrace{Q_{lh}}_\text{latent heat flux} \]
registry = {
    "hfns11a.nc.Z": None,
    "hfls11a.nc.Z": None,
    "hfss11a.nc.Z": None,
    "rss11a.nc.Z": None,
    "rls11a.nc.Z": None
}
POOCH = pooch.create(
    path=pooch.os_cache("Grist_Josey_2003"),
    base_url="ftp://ftp.noc.soton.ac.uk/pub/sxj/clim/netcdf/",
    registry=registry
)

fnames = {fname: POOCH.fetch(fname) for fname in registry}
import unlzw3
def uncompress(input_fname):
    assert input_fname[-2:] == '.Z'
    output_fname = input_fname[:-2]
    with open(input_fname, "rb") as fp:
        uncompressed_data = unlzw3.unlzw(fp.read())
    with open(output_fname, "wb") as fp:
        fp.write(uncompressed_data)
    return output_fname

fnames_uncompressed = [uncompress(f) for f in fnames.values()]
noca = xr.open_mfdataset(fnames_uncompressed, combine='by_coords')
noca = noca.assign_coords({'month': noca.time.dt.month}).swap_dims({'time': 'month'})
noca
/srv/conda/envs/notebook/lib/python3.8/site-packages/xarray/coding/times.py:119: SerializationWarning: Ambiguous reference date string: 2-1-1 0. The first value is assumed to be the year hence will be padded with zeros to remove the ambiguity (the padded reference date string is: 0002-1-1 0). To remove this message, remove the ambiguity by padding your reference date strings with zeros.
  warnings.warn(warning_msg, SerializationWarning)
/srv/conda/envs/notebook/lib/python3.8/site-packages/xarray/coding/times.py:527: SerializationWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using cftime.datetime objects instead, reason: dates out of range
  dtype = _decode_cf_datetime_dtype(data, units, calendar, self.use_cftime)
/srv/conda/envs/notebook/lib/python3.8/site-packages/xarray/core/indexing.py:572: SerializationWarning: Unable to decode time axis into full numpy.datetime64 objects, continuing using cftime.datetime objects instead, reason: dates out of range
  return np.asarray(array[self.key], dtype=None)
<xarray.Dataset>
Dimensions:    (latitude: 170, longitude: 360, month: 12)
Coordinates:
    time       (month) object 0002-01-01 00:00:00 ... 0002-12-01 00:00:00
  * longitude  (longitude) float64 -179.5 -178.5 -177.5 ... 177.5 178.5 179.5
  * latitude   (latitude) float64 -84.5 -83.5 -82.5 -81.5 ... 82.5 83.5 84.5
  * month      (month) int64 1 2 3 4 5 6 7 8 9 10 11 12
Data variables:
    hfls11a    (month, latitude, longitude) float32 dask.array<chunksize=(12, 170, 360), meta=np.ndarray>
    hfns11a    (month, latitude, longitude) float32 dask.array<chunksize=(12, 170, 360), meta=np.ndarray>
    hfss11a    (month, latitude, longitude) float32 dask.array<chunksize=(12, 170, 360), meta=np.ndarray>
    rls11a     (month, latitude, longitude) float32 dask.array<chunksize=(12, 170, 360), meta=np.ndarray>
    rss11a     (month, latitude, longitude) float32 dask.array<chunksize=(12, 170, 360), meta=np.ndarray>
Attributes:
    Conventions:  COARDS
    calendar:     standard
    comments:     Climatological estimate based on ship meteorological report...
    model:        
    center:       National Oceanography Centre, Southampton, UK

Radiative FluxesΒΆ

The radiative fluxes in the earth system are often partitioned into a shortwave and longwave component. These names refer to the wavelength of the electromagnetic waves which constitute the radiation. These components occupy very distinct parts of the electromagnetic spectrum. Radiation emitted from the sun is shortwave radiation, while radiation emitted from the earth itself is longwave radiation. The dramatic difference in wavelengths emitted from each body is a result of Planck’s law of blackbody radiation, which describes how the emission wavelength depends on temperature. Solar radiation has a shorter wavelength than terrestrial because the sun is much hotter than the earth.

Image(url="https://upload.wikimedia.org/wikipedia/commons/7/7c/Atmospheric_Transmission.png")

Atmospheric Transmission. Licensed CC BY-SA 3.0 Creative Commons Attribution-Share Alike 3.0

Via Wikimedia Commons

Image(url="https://upload.wikimedia.org/wikipedia/commons/b/bb/"
      "The-NASA-Earth%27s-Energy-Budget-Poster-Radiant-Energy-System-satellite-infrared-radiation-fluxes.jpg")

By NASA, Public domain. Via Wikimedia Commons

Shortwave RadiationΒΆ

The average value of incoming solar radiation at the top of Earth’s atmosphere is 342 W m\(^{-2}\), although this value varies considerably with latitude and season. Some of the solar radiation is absorbed by the atmosphere or reflected back to space by clouds and aerosols, never reaching the ocean. Furthermore, the ocean doesn’t absorb all of the shortwave radiation that reaches its surface; some is also reflected back to space. The albedo \(\alpha\) describes how much radiation is absorbed versus reflected. Therefore, the shortwave heating is given by

\[ Q_{sw} = (1 - \alpha)Q^{inc}_{sw} \]

where \(Q^{inc}_{sw}\) is the incident shortwave radiation.

AlbedoΒΆ

An albedo of 0 means that all of the incident radiation is absorbed. The ocean’s average albedo is about 0.06, although it does vary somewhat depending on sea state. This relatively low albedo means that the ocean is a very effective absorber of radiation, especially compared to ice. The strong change in albedo that occurs when the high-latitude ocean freezes is melts is therefore a significant climate feedback.

Image(url='https://upload.wikimedia.org/wikipedia/commons/e/e2/Ceres_2004_clear_sky_albedo.png', width=800)

By Giorgiogp2 (Own work). License CC BY-SA 3.0 or GFDL, via Wikimedia Commons

noca.rss11a.hvplot('longitude', 'latitude', clim=(-300, 300), label=noca.rss11a.long_name, cmap='RdBu_r', **opts)
df_zm = noca.fillna(0.).mean(dim=('month', 'longitude')).to_dataframe()
new_columns = {v: re.sub(r'Heat flux (.*) surface \(.*', r'\1', noca[v].long_name) for v in noca.data_vars}
df_zm = df_zm.rename(columns=new_columns)
df_zm.head()
latent net sensible net longwave net shortwave
latitude
-84.5 0.0 0.0 0.0 0.0 0.0
-83.5 0.0 0.0 0.0 0.0 0.0
-82.5 0.0 0.0 0.0 0.0 0.0
-81.5 0.0 0.0 0.0 0.0 0.0
-80.5 0.0 0.0 0.0 0.0 0.0
df_zm.hvplot.line(x='latitude', y='net shortwave', line_width=4, grid=True, legend='top')

Longwave RadiationΒΆ

Because of its much lower temperature, the earth emits radiation in the longwave (i.e. infrared) band. On a global scale, emission is the main way the planet balances the incoming solar radiation to maintain its thermal equilibrium. The power of emitted radiation is strongly dependent on temperature; specifically, the Stefan-Boltzman law states that

\[ Q_{lw}^{emitted} = - \sigma T^4 \]

where \(\sigma = 5.67 \times 10^-8\) W m\(^{-2}\) K\(^{-4}\) is the Stefan-Boltzman constant. (It is important to remember to use absolute temperature in deg. Kelvin when evaluating this formula.) Here we have chosen to maintain the same sign convention for longwave radiation as for shortwave.

\(Q_{lw}^{emitted}\) is not the only contribution to the longwave flux. The atmosphere also emits longwave radiation itself, and some of this radiation is absorbed by the ocean. There is a large amount of cancellation between these two components, but in the net The fact that \(Q_{lw}\) is negative definite means that longwave emissions acts to cool the ocean.

Longwave cooling is indeed a significant sink of heat from the ocean. The mean value of the longwave cooling is around -50 W m\(^{-2}\).

noca.rls11a.hvplot('longitude', 'latitude', clim=(-100, 100), label=noca.rls11a.long_name, cmap='RdBu_r', **opts)
df_zm.hvplot.line(x='latitude', y=['net shortwave', 'net longwave'], line_width=4, grid=True, legend='top', height=400)

Sensible Heat FluxΒΆ

Radiative fluxes transmit heat energy over long distances, e.g.~from the sun to the earth. Sensible heat exchange instead depends on direct molecular contact between air and water. The molecules of the two fluids bump against each other, thereby exchanging heat energy. When the ocean and air temperature are the same, the sensible heat flux is therefore zero. When they differ, heat is exchanged in such as way as to homogenize the temperature. This is analogous to the phenomenon of β€œNewtonian cooling’’ commonly studied in introductory physics courses.

The rate of exchange is highly dependent on the sea state, and in particular on the winds, which generate breaking waves and turbulence. It is easy to imagine how breaking waves enhance the exchange of heat: they literally pull air down into the water (bubbles) and splash water up into the air (sea spray). It is much harder to quantify this process mathematically. Nevertheless, laboratory experiments and field campaigns have permitted us to develop empirical formulas to do so.

Bulk FormulaΒΆ

The commonly accepted formula for sensible heat flux is

\[ Q_s = \rho^{air} c_p^{air} C_H | \Delta \mathbf{u} | (T_{10} - T) \ . \]

The subscript \(_{10}\) refers to the properties of air at 10 m altitude (\(T_{10}\) is air temperature at 10 m). Although this height is somewhat arbitrary, it is a widely-used convention. \(\Delta \mathbf{u} = \mathbf{u}_{10} - \mathbf{u}\) refers to the difference between the wind speed at 10 m and the ocean current speed. \(c_p^{air} = 1030\) J kg\(^{-1}\) K\(^{-1}\) is the specific heat of air and \(\rho^{air}\) is the density of air (approximately \(1.3\) kg m\(^{-3}\)). Note it is the in-situ temperature \(T\), not conservative temperature, which appears in the bulk formula.

Drag CoefficientΒΆ

All of the complexity and difficulty of turbulent air-sea exchange is absorbed into the parameter \(C_H\), the turbulent transfer coefficient for sensible heat exchange. This non-dimensional coefficient is \(O(1000)\) but is a strongly nonlinear function of \(|\Delta \mathbf{u}|\). It also depends on the stability of the stratification within the atmospheric boundary layer. The figure below illustrates this dependence.

Image(filename='04_images/LY09_Drag_Coefficient.png', width=800)
_images/03_air_sea_exchange_46_0.png

Turbulent transfer coefficients as a function of wind speed, taken from Large and Yeager (2009). Note that \(C_H\) is different in stable, (\(C_{H_s}\)) and unstable (\(C_{H_u}\)) atmospheric stratification.

For strong winds, \(C_H\) is a positive function of \(|\Delta \mathbf{u}|\). But as \( | \Delta \mathbf{u} | \) approaches zero, \(C_H\) actually increases very rapidly. This means that the heat exchange doesn’t necessarily vanish for very weak winds, as one might mistakenly conclude from the figure. Further refining our description of \(C_H\), especially for extremely strong winds (i.e.~within hurricanes) is an ongoing topic of study for air-sea-interaction researchers.

noca.hfss11a.hvplot('longitude', 'latitude', clim=(-100, 100), label=noca.hfss11a.long_name, cmap='RdBu_r', **opts)
df_zm.hvplot.line(x='latitude', y=['net shortwave', 'net longwave', 'sensible'], line_width=4, grid=True, legend='top', height=400)

The annual mean contribution of \(Q_s\) to \(Q\) is almost negligible. This is because, on average, the \(T\) and \(T_{10}\) are very close. There is, however, a strong seasonal cycle in sensible heat exchange. Because of the large ocean heat capacity, the ocean is usually colder than the air in summer and warmer than the air in the winter. This drives important seasonal changes in circulation.

Latent Heat FluxΒΆ

The latent heat flux is produced when seawater evaporates to become atmospheric water vapor. This process requires thermodynamic energy and therefore extracts heat from the ocean. The latent heat flux is given by

\[ Q_{lh} = -L_e E \]

where \(E\) is the evaporation rate (in kg m\(^{-2}\) s\(^{-1}\)), and \(L_e \simeq 2.5 \times 10^6\) J kg\(^{-1}\) is the latent heat of vaporization. The fact that \(Q_{lh}\) is negative is due to our sign convention: the latent heat flux always cools the ocean

Evaporation RateΒΆ

What sets the evaporation rate \(E\)? As with sensible heat, evaporation is a turbulent exchange which depends on direct molecular contact between air and water. It is given by another bulk formula:

\[ E = - \rho^{air} c_E | \Delta \mathbf{u} | (q_{10} - q_{sat}) \]

where \(q_{10}\) and \(q_{sat}\) refer, respectively, to the specific humidity of the air at 10 m and the saturation specific humidity right at the sea surface, and \(c_E\) is the exchange coefficient for water vapor. Specific humidity measures the mass fraction of water vapor in air. It is a non-dimensional unit but is frequently reported as g kg\(^{-1}\) (grams of water per kilogram of air).

Saturation Specific HumidtyΒΆ

The maximum amount of water vapor that it can hold is called the saturation specific humidity. This quantity is an exponential function of the air temperature. The relationship between \(q_{sat}\) and temperature is given by the Clausius-Clapeyron relation. A practical formula for evaluating the Clausius-Clapeyron relation over the ocean is

\[ q_{sat}(T) = 0.98 \frac{6.4 \times 10^5 \mbox{ kg m}^{-3}}{\rho^{air}} e^{-(5107.4 K) / T} \ . \]

Right at the sea-surface, the air is always at the same temperature as the ocean (\(T\)) and is always completely saturated; that’s why \(q_{sat}\) is a function of SST. But at 10 m altitude, the air can have a different specific humidity due to atmospheric circulation. If the two values of \(q\) are not equal, the ocean will evaporate at rate \(E\).

noca.hfls11a.hvplot('longitude', 'latitude', clim=(-200, 200), label=noca.hfls11a.long_name, cmap='RdBu_r', **opts)