Part 3: Steric height¶
Andrew Delman. Updated 2023-01-24.
Objectives¶
To use ECCO state output to investigate steric height anomalies.
By the end of this tutorial, you will be able to:
Compute steric height anomaly (SHA)
Compare SHA with sea surface height
Use the GSW-Python module to carry out conversions of temperature and salinity to specific volume/density
Estimate contributions of temperature and salinity to SHA
Introduction¶
Steric height anomaly formulation¶
Though the density of ocean water changes relatively little compared to the density of air and other gases, even relatively small variations in ocean water density have large impacts on the ocean circulation and sea level variations. Under the conditions of hydrostatic balance (conditions assumed by the MITgcm model configuration run in ECCO v4r4), the vertical gradient of pressure is a function of density and gravitational acceleration :
When the density of a parcel water changes, e.g. because its temperature or salinity changes, the parcel of water expands or contracts, and this has an effect on the vertical height of the parcel. This can be expressed in terms of the change in height between two pressure surfaces.
or in integral form
which can also be notated in terms of the specific volume
(cf. Gill ch. 7.7, or discussion of thickness in Vallis ch. 2.6)
where is the steric height at pressure level relative to some reference pressure level . The atmospheric analogue to steric height is called thickness, and is commonly used in meteorology and atmospheric science. Generally we are less interested in the value of the steric height itself than in its spatial or temporal variation, so the steric height anomaly can be defined in terms of a specific volume anomaly
where is the anomaly from the a reference specific volume . Sometimes the literature also refers to the geopotential anomaly, which is the steric height anomaly times (i.e., the above equation without dividing by ) and has units m s.
GSW-Python module¶
To help carry out computations involving seawater density and related quantities such as specific volume, we will use the GSW-Python package, an implementation of the Gibbs SeaWater (GSW) Oceanographic Toolbox of TEOS-10. This especially helpful in computations involving the nonlinear thermodynamic equation of seawater (also called the equation of state) that relates temperature, salinity, pressure, and density. This equation is quite complicated (the formulation of density for example has 75 terms!), so it is very helpful to have a package to help us with these computations. If you don’t have GSW-Python already, you can install it using “conda install -c conda-forge gsw” or “pip install gsw”.
Open files¶
[1]:
# first import needed packages
import numpy as np
import xarray as xr
import xmitgcm
import xgcm
import glob
from os.path import expanduser,join
import sys
user_home_dir = expanduser('~')
sys.path.append(join(user_home_dir,'ECCOv4-py')) # only needed if ecco_v4_py files are stored under this directory
import matplotlib.pyplot as plt
import ecco_v4_py as ecco
from ecco_po_tutorials import *
import gsw
Grid and density/pressure files¶
If you’ve completed the previous tutorials you are quite familiar with these files by now.
[2]:
# download root (parent) directory
download_root_dir = join(user_home_dir,'Downloads','ECCO_V4r4_PODAAC')
# ShortNames
grid_params_shortname = "ECCO_L4_GEOMETRY_LLC0090GRID_V4R4"
denspress_monthly_shortname = "ECCO_L4_DENS_STRAT_PRESS_LLC0090GRID_MONTHLY_V4R4"
# grid parameters file
grid_params_file = "GRID_GEOMETRY_ECCO_V4r4_native_llc0090.nc"
grid_params_file_path = join(download_root_dir,grid_params_shortname,grid_params_file)
ds_grid = xr.open_dataset(grid_params_file_path)
# density/pressure file
download_dir = join(download_root_dir,denspress_monthly_shortname)
curr_denspress_file = list(glob.glob(join(download_dir,'*2000-01*.nc')))
ds_denspress = xr.open_dataset(curr_denspress_file[0])
Sea surface height file¶
To look at the impact of steric height variations, we will also be looking at the actual sea surface height field from ECCOv4. To compare steric height with sea surface height, we of course need to download and retrieve sea surface height output. You can do this by importing the ecco_download module and calling the function ecco_podaac_download (see the Geostrophic Balance tutorial for an example). Consulting the variable
list for monthly mean output, we find that the datasets containing sea surface height have ShortName ECCO_L4_SSH_LLC0090GRID_MONTHLY_V4R4
. To start, download the granule of that dataset corresponding to January 2000 (2000-01). Once you have this file downloaded, view its structure using the code below.
[3]:
SSH_monthly_shortname = "ECCO_L4_SSH_LLC0090GRID_MONTHLY_V4R4"
download_dir = join(download_root_dir,SSH_monthly_shortname)
curr_SSH_file = list(glob.glob(join(download_dir,'*2000-01*.nc')))
ds_SSH = xr.open_dataset(curr_SSH_file[0])
ds_SSH
[3]:
<xarray.Dataset> Dimensions: (i: 90, i_g: 90, j: 90, j_g: 90, tile: 13, time: 1, nv: 2, nb: 4) Coordinates: (12/13) * i (i) int32 0 1 2 3 4 5 6 7 8 9 ... 80 81 82 83 84 85 86 87 88 89 * i_g (i_g) int32 0 1 2 3 4 5 6 7 8 9 ... 80 81 82 83 84 85 86 87 88 89 * j (j) int32 0 1 2 3 4 5 6 7 8 9 ... 80 81 82 83 84 85 86 87 88 89 * j_g (j_g) int32 0 1 2 3 4 5 6 7 8 9 ... 80 81 82 83 84 85 86 87 88 89 * tile (tile) int32 0 1 2 3 4 5 6 7 8 9 10 11 12 * time (time) datetime64[ns] 2000-01-16T12:00:00 ... ... YC (tile, j, i) float32 ... XG (tile, j_g, i_g) float32 ... YG (tile, j_g, i_g) float32 ... time_bnds (time, nv) datetime64[ns] 2000-01-01 2000-02-01 XC_bnds (tile, j, i, nb) float32 ... YC_bnds (tile, j, i, nb) float32 ... Dimensions without coordinates: nv, nb Data variables: SSH (time, tile, j, i) float32 ... SSHIBC (time, tile, j, i) float32 ... SSHNOIBC (time, tile, j, i) float32 ... ETAN (time, tile, j, i) float32 ... Attributes: (12/57) acknowledgement: This research was carried out by the Jet Pr... author: Ian Fenty and Ou Wang cdm_data_type: Grid comment: Fields provided on the curvilinear lat-lon-... Conventions: CF-1.8, ACDD-1.3 coordinates_comment: Note: the global 'coordinates' attribute de... ... ... time_coverage_duration: P1M time_coverage_end: 2000-02-01T00:00:00 time_coverage_resolution: P1M time_coverage_start: 2000-01-01T00:00:00 title: ECCO Sea Surface Height - Monthly Mean llc9... uuid: a7c2a1c4-400c-11eb-9f79-0cc47a3f49c3
- i: 90
- i_g: 90
- j: 90
- j_g: 90
- tile: 13
- time: 1
- nv: 2
- nb: 4
- i(i)int320 1 2 3 4 5 6 ... 84 85 86 87 88 89
- axis :
- X
- long_name :
- grid index in x for variables at tracer and 'v' locations
- swap_dim :
- XC
- comment :
- In the Arakawa C-grid system, tracer (e.g., THETA) and 'v' variables (e.g., VVEL) have the same x coordinate on the model grid.
- coverage_content_type :
- coordinate
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89])
- i_g(i_g)int320 1 2 3 4 5 6 ... 84 85 86 87 88 89
- axis :
- X
- long_name :
- grid index in x for variables at 'u' and 'g' locations
- c_grid_axis_shift :
- -0.5
- swap_dim :
- XG
- comment :
- In the Arakawa C-grid system, 'u' (e.g., UVEL) and 'g' variables (e.g., XG) have the same x coordinate on the model grid.
- coverage_content_type :
- coordinate
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89])
- j(j)int320 1 2 3 4 5 6 ... 84 85 86 87 88 89
- axis :
- Y
- long_name :
- grid index in y for variables at tracer and 'u' locations
- swap_dim :
- YC
- comment :
- In the Arakawa C-grid system, tracer (e.g., THETA) and 'u' variables (e.g., UVEL) have the same y coordinate on the model grid.
- coverage_content_type :
- coordinate
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89])
- j_g(j_g)int320 1 2 3 4 5 6 ... 84 85 86 87 88 89
- axis :
- Y
- long_name :
- grid index in y for variables at 'v' and 'g' locations
- c_grid_axis_shift :
- -0.5
- swap_dim :
- YG
- comment :
- In the Arakawa C-grid system, 'v' (e.g., VVEL) and 'g' variables (e.g., XG) have the same y coordinate.
- coverage_content_type :
- coordinate
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89])
- tile(tile)int320 1 2 3 4 5 6 7 8 9 10 11 12
- long_name :
- lat-lon-cap tile index
- comment :
- The ECCO V4 horizontal model grid is divided into 13 tiles of 90x90 cells for convenience.
- coverage_content_type :
- coordinate
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
- time(time)datetime64[ns]2000-01-16T12:00:00
- long_name :
- center time of averaging period
- axis :
- T
- bounds :
- time_bnds
- coverage_content_type :
- coordinate
- standard_name :
- time
array(['2000-01-16T12:00:00.000000000'], dtype='datetime64[ns]')
- XC(tile, j, i)float32...
- long_name :
- longitude of tracer grid cell center
- units :
- degrees_east
- coordinate :
- YC XC
- bounds :
- XC_bnds
- comment :
- nonuniform grid spacing
- coverage_content_type :
- coordinate
- standard_name :
- longitude
[105300 values with dtype=float32]
- YC(tile, j, i)float32...
- long_name :
- latitude of tracer grid cell center
- units :
- degrees_north
- coordinate :
- YC XC
- bounds :
- YC_bnds
- comment :
- nonuniform grid spacing
- coverage_content_type :
- coordinate
- standard_name :
- latitude
[105300 values with dtype=float32]
- XG(tile, j_g, i_g)float32...
- long_name :
- longitude of 'southwest' corner of tracer grid cell
- units :
- degrees_east
- coordinate :
- YG XG
- comment :
- Nonuniform grid spacing. Note: 'southwest' does not correspond to geographic orientation but is used for convenience to describe the computational grid. See MITgcm dcoumentation for details.
- coverage_content_type :
- coordinate
- standard_name :
- longitude
[105300 values with dtype=float32]
- YG(tile, j_g, i_g)float32...
- long_name :
- latitude of 'southwest' corner of tracer grid cell
- units :
- degrees_north
- coordinate :
- YG XG
- comment :
- Nonuniform grid spacing. Note: 'southwest' does not correspond to geographic orientation but is used for convenience to describe the computational grid. See MITgcm dcoumentation for details.
- coverage_content_type :
- coordinate
- standard_name :
- latitude
[105300 values with dtype=float32]
- time_bnds(time, nv)datetime64[ns]...
- comment :
- Start and end times of averaging period.
- coverage_content_type :
- coordinate
- long_name :
- time bounds of averaging period
array([['2000-01-01T00:00:00.000000000', '2000-02-01T00:00:00.000000000']], dtype='datetime64[ns]')
- XC_bnds(tile, j, i, nb)float32...
- comment :
- Bounds array follows CF conventions. XC_bnds[i,j,0] = 'southwest' corner (j-1, i-1), XC_bnds[i,j,1] = 'southeast' corner (j-1, i+1), XC_bnds[i,j,2] = 'northeast' corner (j+1, i+1), XC_bnds[i,j,3] = 'northwest' corner (j+1, i-1). Note: 'southwest', 'southeast', northwest', and 'northeast' do not correspond to geographic orientation but are used for convenience to describe the computational grid. See MITgcm dcoumentation for details.
- coverage_content_type :
- coordinate
- long_name :
- longitudes of tracer grid cell corners
[421200 values with dtype=float32]
- YC_bnds(tile, j, i, nb)float32...
- comment :
- Bounds array follows CF conventions. YC_bnds[i,j,0] = 'southwest' corner (j-1, i-1), YC_bnds[i,j,1] = 'southeast' corner (j-1, i+1), YC_bnds[i,j,2] = 'northeast' corner (j+1, i+1), YC_bnds[i,j,3] = 'northwest' corner (j+1, i-1). Note: 'southwest', 'southeast', northwest', and 'northeast' do not correspond to geographic orientation but are used for convenience to describe the computational grid. See MITgcm dcoumentation for details.
- coverage_content_type :
- coordinate
- long_name :
- latitudes of tracer grid cell corners
[421200 values with dtype=float32]
- SSH(time, tile, j, i)float32...
- long_name :
- Dynamic sea surface height anomaly
- units :
- m
- coverage_content_type :
- modelResult
- standard_name :
- sea_surface_height_above_geoid
- comment :
- Dynamic sea surface height anomaly above the geoid, suitable for comparisons with altimetry sea surface height data products that apply the inverse barometer (IB) correction. Note: SSH is calculated by correcting model sea level anomaly ETAN for three effects: a) global mean steric sea level changes related to density changes in the Boussinesq volume-conserving model (Greatbatch correction, see sterGloH), b) the inverted barometer (IB) effect (see SSHIBC) and c) sea level displacement due to sea-ice and snow pressure loading (see sIceLoad). SSH can be compared with the similarly-named SSH variable in previous ECCO products that did not include atmospheric pressure loading (e.g., Version 4 Release 3). Use SSHNOIBC for comparisons with altimetry data products that do NOT apply the IB correction.
- valid_min :
- -1.8805772066116333
- valid_max :
- 1.4207719564437866
[105300 values with dtype=float32]
- SSHIBC(time, tile, j, i)float32...
- long_name :
- The inverted barometer (IB) correction to sea surface height due to atmospheric pressure loading
- units :
- m
- coverage_content_type :
- modelResult
- comment :
- Not an SSH itself, but a correction to model sea level anomaly (ETAN) required to account for the static part of sea surface displacement by atmosphere pressure loading: SSH = SSHNOIBC - SSHIBC. Note: Use SSH for model-data comparisons with altimetry data products that DO apply the IB correction and SSHNOIBC for comparisons with altimetry data products that do NOT apply the IB correction.
- valid_min :
- -0.30144819617271423
- valid_max :
- 0.5248842239379883
[105300 values with dtype=float32]
- SSHNOIBC(time, tile, j, i)float32...
- long_name :
- Sea surface height anomaly without the inverted barometer (IB) correction
- units :
- m
- coverage_content_type :
- modelResult
- comment :
- Sea surface height anomaly above the geoid without the inverse barometer (IB) correction, suitable for comparisons with altimetry sea surface height data products that do NOT apply the inverse barometer (IB) correction. Note: SSHNOIBC is calculated by correcting model sea level anomaly ETAN for two effects: a) global mean steric sea level changes related to density changes in the Boussinesq volume-conserving model (Greatbatch correction, see sterGloH), b) sea level displacement due to sea-ice and snow pressure loading (see sIceLoad). In ECCO Version 4 Release 4 the model is forced with atmospheric pressure loading. SSHNOIBC does not correct for the static part of the effect of atmosphere pressure loading on sea surface height (the so-called inverse barometer (IB) correction). Use SSH for comparisons with altimetry data products that DO apply the IB correction.
- valid_min :
- -1.6654272079467773
- valid_max :
- 1.4550364017486572
[105300 values with dtype=float32]
- ETAN(time, tile, j, i)float32...
- long_name :
- Model sea level anomaly
- units :
- m
- coverage_content_type :
- modelResult
- comment :
- Model sea level anomaly WITHOUT corrections for global mean density (steric) changes, inverted barometer effect, or volume displacement due to submerged sea-ice and snow . Note: ETAN should NOT be used for comparisons with altimetry data products because ETAN is NOT corrected for (a) global mean steric sea level changes related to density changes in the Boussinesq volume-conserving model (Greatbatch correction, see sterGloH) nor (b) sea level displacement due to submerged sea-ice and snow (see sIceLoad). These corrections ARE made for the variables SSH and SSHNOIBC.
- valid_min :
- -8.304216384887695
- valid_max :
- 1.460192084312439
[105300 values with dtype=float32]
- acknowledgement :
- This research was carried out by the Jet Propulsion Laboratory, managed by the California Institute of Technology under a contract with the National Aeronautics and Space Administration.
- author :
- Ian Fenty and Ou Wang
- cdm_data_type :
- Grid
- comment :
- Fields provided on the curvilinear lat-lon-cap 90 (llc90) native grid used in the ECCO model. SSH (dynamic sea surface height) = SSHNOIBC (dynamic sea surface without the inverse barometer correction) - SSHIBC (inverse barometer correction). The inverted barometer correction accounts for variations in sea surface height due to atmospheric pressure variations. Note: ETAN is model sea level anomaly and should not be compared with satellite altimetery products, see SSH and ETAN for more details.
- Conventions :
- CF-1.8, ACDD-1.3
- coordinates_comment :
- Note: the global 'coordinates' attribute describes auxillary coordinates.
- creator_email :
- ecco-group@mit.edu
- creator_institution :
- NASA Jet Propulsion Laboratory (JPL)
- creator_name :
- ECCO Consortium
- creator_type :
- group
- creator_url :
- https://ecco-group.org
- date_created :
- 2020-12-16T18:07:46
- date_issued :
- 2020-12-16T18:07:46
- date_metadata_modified :
- 2021-03-15T21:54:47
- date_modified :
- 2021-03-15T21:54:47
- geospatial_bounds_crs :
- EPSG:4326
- geospatial_lat_max :
- 90.0
- geospatial_lat_min :
- -90.0
- geospatial_lat_resolution :
- variable
- geospatial_lat_units :
- degrees_north
- geospatial_lon_max :
- 180.0
- geospatial_lon_min :
- -180.0
- geospatial_lon_resolution :
- variable
- geospatial_lon_units :
- degrees_east
- history :
- Inaugural release of an ECCO Central Estimate solution to PO.DAAC
- id :
- 10.5067/ECL5M-SSH44
- institution :
- NASA Jet Propulsion Laboratory (JPL)
- instrument_vocabulary :
- GCMD instrument keywords
- keywords :
- EARTH SCIENCE > OCEANS > SEA SURFACE TOPOGRAPHY > SEA SURFACE HEIGHT, EARTH SCIENCE SERVICES > MODELS > EARTH SCIENCE REANALYSES/ASSIMILATION MODELS
- keywords_vocabulary :
- NASA Global Change Master Directory (GCMD) Science Keywords
- license :
- Public Domain
- metadata_link :
- https://cmr.earthdata.nasa.gov/search/collections.umm_json?ShortName=ECCO_L4_SSH_LLC0090GRID_MONTHLY_V4R4
- naming_authority :
- gov.nasa.jpl
- platform :
- ERS-1/2, TOPEX/Poseidon, Geosat Follow-On (GFO), ENVISAT, Jason-1, Jason-2, CryoSat-2, SARAL/AltiKa, Jason-3, AVHRR, Aquarius, SSM/I, SSMIS, GRACE, DTU17MDT, Argo, WOCE, GO-SHIP, MEOP, Ice Tethered Profilers (ITP)
- platform_vocabulary :
- GCMD platform keywords
- processing_level :
- L4
- product_name :
- SEA_SURFACE_HEIGHT_mon_mean_2000-01_ECCO_V4r4_native_llc0090.nc
- product_time_coverage_end :
- 2018-01-01T00:00:00
- product_time_coverage_start :
- 1992-01-01T12:00:00
- product_version :
- Version 4, Release 4
- program :
- NASA Physical Oceanography, Cryosphere, Modeling, Analysis, and Prediction (MAP)
- project :
- Estimating the Circulation and Climate of the Ocean (ECCO)
- publisher_email :
- podaac@podaac.jpl.nasa.gov
- publisher_institution :
- PO.DAAC
- publisher_name :
- Physical Oceanography Distributed Active Archive Center (PO.DAAC)
- publisher_type :
- institution
- publisher_url :
- https://podaac.jpl.nasa.gov
- references :
- ECCO Consortium, Fukumori, I., Wang, O., Fenty, I., Forget, G., Heimbach, P., & Ponte, R. M. 2020. Synopsis of the ECCO Central Production Global Ocean and Sea-Ice State Estimate (Version 4 Release 4). doi:10.5281/zenodo.3765928
- source :
- The ECCO V4r4 state estimate was produced by fitting a free-running solution of the MITgcm (checkpoint 66g) to satellite and in situ observational data in a least squares sense using the adjoint method
- standard_name_vocabulary :
- NetCDF Climate and Forecast (CF) Metadata Convention
- summary :
- This dataset provides monthly-averaged dynamic sea surface height and model sea level anomaly on the lat-lon-cap 90 (llc90) native model grid from the ECCO Version 4 Release 4 (V4r4) ocean and sea-ice state estimate. Estimating the Circulation and Climate of the Ocean (ECCO) state estimates are dynamically and kinematically-consistent reconstructions of the three-dimensional, time-evolving ocean, sea-ice, and surface atmospheric states. ECCO V4r4 is a free-running solution of a global, nominally 1-degree configuration of the MIT general circulation model (MITgcm) that has been fit to observations in a least-squares sense. Observational data constraints used in V4r4 include sea surface height (SSH) from satellite altimeters [ERS-1/2, TOPEX/Poseidon, GFO, ENVISAT, Jason-1,2,3, CryoSat-2, and SARAL/AltiKa]; sea surface temperature (SST) from satellite radiometers [AVHRR], sea surface salinity (SSS) from the Aquarius satellite radiometer/scatterometer, ocean bottom pressure (OBP) from the GRACE satellite gravimeter; sea-ice concentration from satellite radiometers [SSM/I and SSMIS], and in-situ ocean temperature and salinity measured with conductivity-temperature-depth (CTD) sensors and expendable bathythermographs (XBTs) from several programs [e.g., WOCE, GO-SHIP, Argo, and others] and platforms [e.g., research vessels, gliders, moorings, ice-tethered profilers, and instrumented pinnipeds]. V4r4 covers the period 1992-01-01T12:00:00 to 2018-01-01T00:00:00.
- time_coverage_duration :
- P1M
- time_coverage_end :
- 2000-02-01T00:00:00
- time_coverage_resolution :
- P1M
- time_coverage_start :
- 2000-01-01T00:00:00
- title :
- ECCO Sea Surface Height - Monthly Mean llc90 Grid (Version 4 Release 4)
- uuid :
- a7c2a1c4-400c-11eb-9f79-0cc47a3f49c3
Of the data variables here, there are three forms of sea surface height (SSH
, SSHNOIBC
, ETAN
) with slight but important differences, and one variable SSHIBC
which is a correction for atmospheric pressure effects. Of the three sea surface height variables, the dynamic sea surface height anomaly SSH
is the most directly related to ocean dynamics (e.g., near-surface geostrophic velocities). The dynamic SSH is also the variable that we would use for comparison with many satellite
SSH products such as the Copernicus and NASA MEaSUREs gridded products. Hence SSH
is the variable that we will use in this tutorial for comparison with the steric height anomaly.
Temperature and salinity file¶
Later in this tutorial we will also consider the distinct contributions of temperature and salinity to steric height variations. Fortunately for us, the potential temperature and salinity fields are contained in the same dataset, so for the Jan 2000 temperature and salinity we will only need to download and open one file. Again, use ecco_podaac_download to download the file with ShortName ECCO_L4_TEMP_SALINITY_LLC0090GRID_MONTHLY_V4R4
for Jan 2000. Then we can open this file:
[4]:
TS_monthly_shortname = "ECCO_L4_TEMP_SALINITY_LLC0090GRID_MONTHLY_V4R4"
download_dir = join(download_root_dir,TS_monthly_shortname)
curr_TS_file = list(glob.glob(join(download_dir,'*2000-01*.nc')))
ds_TS = xr.open_dataset(curr_TS_file[0])
ds_TS
[4]:
<xarray.Dataset> Dimensions: (i: 90, i_g: 90, j: 90, j_g: 90, k: 50, k_u: 50, k_l: 50, k_p1: 51, tile: 13, time: 1, nv: 2, nb: 4) Coordinates: (12/22) * i (i) int32 0 1 2 3 4 5 6 7 8 9 ... 80 81 82 83 84 85 86 87 88 89 * i_g (i_g) int32 0 1 2 3 4 5 6 7 8 9 ... 80 81 82 83 84 85 86 87 88 89 * j (j) int32 0 1 2 3 4 5 6 7 8 9 ... 80 81 82 83 84 85 86 87 88 89 * j_g (j_g) int32 0 1 2 3 4 5 6 7 8 9 ... 80 81 82 83 84 85 86 87 88 89 * k (k) int32 0 1 2 3 4 5 6 7 8 9 ... 40 41 42 43 44 45 46 47 48 49 * k_u (k_u) int32 0 1 2 3 4 5 6 7 8 9 ... 40 41 42 43 44 45 46 47 48 49 ... ... Zu (k_u) float32 -10.0 -20.0 -30.0 ... -5.678e+03 -6.134e+03 Zl (k_l) float32 0.0 -10.0 -20.0 ... -5.244e+03 -5.678e+03 time_bnds (time, nv) datetime64[ns] 2000-01-01 2000-02-01 XC_bnds (tile, j, i, nb) float32 ... YC_bnds (tile, j, i, nb) float32 ... Z_bnds (k, nv) float32 0.0 -10.0 -10.0 ... -5.678e+03 -6.134e+03 Dimensions without coordinates: nv, nb Data variables: THETA (time, k, tile, j, i) float32 ... SALT (time, k, tile, j, i) float32 ... Attributes: (12/62) acknowledgement: This research was carried out by the Jet... author: Ian Fenty and Ou Wang cdm_data_type: Grid comment: Fields provided on the curvilinear lat-l... Conventions: CF-1.8, ACDD-1.3 coordinates_comment: Note: the global 'coordinates' attribute... ... ... time_coverage_duration: P1M time_coverage_end: 2000-02-01T00:00:00 time_coverage_resolution: P1M time_coverage_start: 2000-01-01T00:00:00 title: ECCO Ocean Temperature and Salinity - Mo... uuid: f72f9b24-4181-11eb-bf7d-0cc47a3f4871
- i: 90
- i_g: 90
- j: 90
- j_g: 90
- k: 50
- k_u: 50
- k_l: 50
- k_p1: 51
- tile: 13
- time: 1
- nv: 2
- nb: 4
- i(i)int320 1 2 3 4 5 6 ... 84 85 86 87 88 89
- axis :
- X
- long_name :
- grid index in x for variables at tracer and 'v' locations
- swap_dim :
- XC
- comment :
- In the Arakawa C-grid system, tracer (e.g., THETA) and 'v' variables (e.g., VVEL) have the same x coordinate on the model grid.
- coverage_content_type :
- coordinate
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89])
- i_g(i_g)int320 1 2 3 4 5 6 ... 84 85 86 87 88 89
- axis :
- X
- long_name :
- grid index in x for variables at 'u' and 'g' locations
- c_grid_axis_shift :
- -0.5
- swap_dim :
- XG
- comment :
- In the Arakawa C-grid system, 'u' (e.g., UVEL) and 'g' variables (e.g., XG) have the same x coordinate on the model grid.
- coverage_content_type :
- coordinate
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89])
- j(j)int320 1 2 3 4 5 6 ... 84 85 86 87 88 89
- axis :
- Y
- long_name :
- grid index in y for variables at tracer and 'u' locations
- swap_dim :
- YC
- comment :
- In the Arakawa C-grid system, tracer (e.g., THETA) and 'u' variables (e.g., UVEL) have the same y coordinate on the model grid.
- coverage_content_type :
- coordinate
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89])
- j_g(j_g)int320 1 2 3 4 5 6 ... 84 85 86 87 88 89
- axis :
- Y
- long_name :
- grid index in y for variables at 'v' and 'g' locations
- c_grid_axis_shift :
- -0.5
- swap_dim :
- YG
- comment :
- In the Arakawa C-grid system, 'v' (e.g., VVEL) and 'g' variables (e.g., XG) have the same y coordinate.
- coverage_content_type :
- coordinate
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89])
- k(k)int320 1 2 3 4 5 6 ... 44 45 46 47 48 49
- axis :
- Z
- long_name :
- grid index in z for tracer variables
- swap_dim :
- Z
- coverage_content_type :
- coordinate
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49])
- k_u(k_u)int320 1 2 3 4 5 6 ... 44 45 46 47 48 49
- axis :
- Z
- c_grid_axis_shift :
- 0.5
- swap_dim :
- Zu
- coverage_content_type :
- coordinate
- long_name :
- grid index in z corresponding to the bottom face of tracer grid cells ('w' locations)
- comment :
- First index corresponds to the bottom surface of the uppermost tracer grid cell. The use of 'u' in the variable name follows the MITgcm convention for ocean variables in which the upper (u) face of a tracer grid cell on the logical grid corresponds to the bottom face of the grid cell on the physical grid.
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49])
- k_l(k_l)int320 1 2 3 4 5 6 ... 44 45 46 47 48 49
- axis :
- Z
- c_grid_axis_shift :
- -0.5
- swap_dim :
- Zl
- coverage_content_type :
- coordinate
- long_name :
- grid index in z corresponding to the top face of tracer grid cells ('w' locations)
- comment :
- First index corresponds to the top surface of the uppermost tracer grid cell. The use of 'l' in the variable name follows the MITgcm convention for ocean variables in which the lower (l) face of a tracer grid cell on the logical grid corresponds to the top face of the grid cell on the physical grid.
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49])
- k_p1(k_p1)int320 1 2 3 4 5 6 ... 45 46 47 48 49 50
- axis :
- Z
- long_name :
- grid index in z for variables at 'w' locations
- c_grid_axis_shift :
- [-0.5 0.5]
- swap_dim :
- Zp1
- comment :
- Includes top of uppermost model tracer cell (k_p1=0) and bottom of lowermost tracer cell (k_p1=51).
- coverage_content_type :
- coordinate
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50])
- tile(tile)int320 1 2 3 4 5 6 7 8 9 10 11 12
- long_name :
- lat-lon-cap tile index
- comment :
- The ECCO V4 horizontal model grid is divided into 13 tiles of 90x90 cells for convenience.
- coverage_content_type :
- coordinate
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
- time(time)datetime64[ns]2000-01-16T12:00:00
- long_name :
- center time of averaging period
- axis :
- T
- bounds :
- time_bnds
- coverage_content_type :
- coordinate
- standard_name :
- time
array(['2000-01-16T12:00:00.000000000'], dtype='datetime64[ns]')
- XC(tile, j, i)float32...
- long_name :
- longitude of tracer grid cell center
- units :
- degrees_east
- coordinate :
- YC XC
- bounds :
- XC_bnds
- comment :
- nonuniform grid spacing
- coverage_content_type :
- coordinate
- standard_name :
- longitude
[105300 values with dtype=float32]
- YC(tile, j, i)float32...
- long_name :
- latitude of tracer grid cell center
- units :
- degrees_north
- coordinate :
- YC XC
- bounds :
- YC_bnds
- comment :
- nonuniform grid spacing
- coverage_content_type :
- coordinate
- standard_name :
- latitude
[105300 values with dtype=float32]
- XG(tile, j_g, i_g)float32...
- long_name :
- longitude of 'southwest' corner of tracer grid cell
- units :
- degrees_east
- coordinate :
- YG XG
- comment :
- Nonuniform grid spacing. Note: 'southwest' does not correspond to geographic orientation but is used for convenience to describe the computational grid. See MITgcm dcoumentation for details.
- coverage_content_type :
- coordinate
- standard_name :
- longitude
[105300 values with dtype=float32]
- YG(tile, j_g, i_g)float32...
- long_name :
- latitude of 'southwest' corner of tracer grid cell
- units :
- degrees_north
- coordinate :
- YG XG
- comment :
- Nonuniform grid spacing. Note: 'southwest' does not correspond to geographic orientation but is used for convenience to describe the computational grid. See MITgcm dcoumentation for details.
- coverage_content_type :
- coordinate
- standard_name :
- latitude
[105300 values with dtype=float32]
- Z(k)float32...
- long_name :
- depth of tracer grid cell center
- units :
- m
- positive :
- up
- bounds :
- Z_bnds
- comment :
- Non-uniform vertical spacing.
- coverage_content_type :
- coordinate
- standard_name :
- depth
array([-5.000000e+00, -1.500000e+01, -2.500000e+01, -3.500000e+01, -4.500000e+01, -5.500000e+01, -6.500000e+01, -7.500500e+01, -8.502500e+01, -9.509500e+01, -1.053100e+02, -1.158700e+02, -1.271500e+02, -1.397400e+02, -1.544700e+02, -1.724000e+02, -1.947350e+02, -2.227100e+02, -2.574700e+02, -2.999300e+02, -3.506800e+02, -4.099300e+02, -4.774700e+02, -5.527100e+02, -6.347350e+02, -7.224000e+02, -8.144700e+02, -9.097400e+02, -1.007155e+03, -1.105905e+03, -1.205535e+03, -1.306205e+03, -1.409150e+03, -1.517095e+03, -1.634175e+03, -1.765135e+03, -1.914150e+03, -2.084035e+03, -2.276225e+03, -2.491250e+03, -2.729250e+03, -2.990250e+03, -3.274250e+03, -3.581250e+03, -3.911250e+03, -4.264250e+03, -4.640250e+03, -5.039250e+03, -5.461250e+03, -5.906250e+03], dtype=float32)
- Zp1(k_p1)float32...
- long_name :
- depth of tracer grid cell interface
- units :
- m
- positive :
- up
- comment :
- Contains one element more than the number of vertical layers. First element is 0m, the depth of the upper interface of the surface grid cell. Last element is the depth of the lower interface of the deepest grid cell.
- coverage_content_type :
- coordinate
- standard_name :
- depth
array([ 0. , -10. , -20. , -30. , -40. , -50. , -60. , -70. , -80.01, -90.04, -100.15, -110.47, -121.27, -133.03, -146.45, -162.49, -182.31, -207.16, -238.26, -276.68, -323.18, -378.18, -441.68, -513.26, -592.16, -677.31, -767.49, -861.45, -958.03, -1056.28, -1155.53, -1255.54, -1356.87, -1461.43, -1572.76, -1695.59, -1834.68, -1993.62, -2174.45, -2378. , -2604.5 , -2854. , -3126.5 , -3422. , -3740.5 , -4082. , -4446.5 , -4834. , -5244.5 , -5678. , -6134.5 ], dtype=float32)
- Zu(k_u)float32...
- units :
- m
- positive :
- up
- coverage_content_type :
- coordinate
- standard_name :
- depth
- long_name :
- depth of the bottom face of tracer grid cells
- comment :
- First element is -10m, the depth of the bottom face of the first tracer grid cell. Last element is the depth of the bottom face of the deepest grid cell. The use of 'u' in the variable name follows the MITgcm convention for ocean variables in which the upper (u) face of a tracer grid cell on the logical grid corresponds to the bottom face of the grid cell on the physical grid. In other words, the logical vertical grid of MITgcm ocean variables is inverted relative to the physical vertical grid.
array([ -10. , -20. , -30. , -40. , -50. , -60. , -70. , -80.01, -90.04, -100.15, -110.47, -121.27, -133.03, -146.45, -162.49, -182.31, -207.16, -238.26, -276.68, -323.18, -378.18, -441.68, -513.26, -592.16, -677.31, -767.49, -861.45, -958.03, -1056.28, -1155.53, -1255.54, -1356.87, -1461.43, -1572.76, -1695.59, -1834.68, -1993.62, -2174.45, -2378. , -2604.5 , -2854. , -3126.5 , -3422. , -3740.5 , -4082. , -4446.5 , -4834. , -5244.5 , -5678. , -6134.5 ], dtype=float32)
- Zl(k_l)float32...
- units :
- m
- positive :
- up
- coverage_content_type :
- coordinate
- standard_name :
- depth
- long_name :
- depth of the top face of tracer grid cells
- comment :
- First element is 0m, the depth of the top face of the first tracer grid cell (ocean surface). Last element is the depth of the top face of the deepest grid cell. The use of 'l' in the variable name follows the MITgcm convention for ocean variables in which the lower (l) face of a tracer grid cell on the logical grid corresponds to the top face of the grid cell on the physical grid. In other words, the logical vertical grid of MITgcm ocean variables is inverted relative to the physical vertical grid.
array([ 0. , -10. , -20. , -30. , -40. , -50. , -60. , -70. , -80.01, -90.04, -100.15, -110.47, -121.27, -133.03, -146.45, -162.49, -182.31, -207.16, -238.26, -276.68, -323.18, -378.18, -441.68, -513.26, -592.16, -677.31, -767.49, -861.45, -958.03, -1056.28, -1155.53, -1255.54, -1356.87, -1461.43, -1572.76, -1695.59, -1834.68, -1993.62, -2174.45, -2378. , -2604.5 , -2854. , -3126.5 , -3422. , -3740.5 , -4082. , -4446.5 , -4834. , -5244.5 , -5678. ], dtype=float32)
- time_bnds(time, nv)datetime64[ns]...
- comment :
- Start and end times of averaging period.
- coverage_content_type :
- coordinate
- long_name :
- time bounds of averaging period
array([['2000-01-01T00:00:00.000000000', '2000-02-01T00:00:00.000000000']], dtype='datetime64[ns]')
- XC_bnds(tile, j, i, nb)float32...
- comment :
- Bounds array follows CF conventions. XC_bnds[i,j,0] = 'southwest' corner (j-1, i-1), XC_bnds[i,j,1] = 'southeast' corner (j-1, i+1), XC_bnds[i,j,2] = 'northeast' corner (j+1, i+1), XC_bnds[i,j,3] = 'northwest' corner (j+1, i-1). Note: 'southwest', 'southeast', northwest', and 'northeast' do not correspond to geographic orientation but are used for convenience to describe the computational grid. See MITgcm dcoumentation for details.
- coverage_content_type :
- coordinate
- long_name :
- longitudes of tracer grid cell corners
[421200 values with dtype=float32]
- YC_bnds(tile, j, i, nb)float32...
- comment :
- Bounds array follows CF conventions. YC_bnds[i,j,0] = 'southwest' corner (j-1, i-1), YC_bnds[i,j,1] = 'southeast' corner (j-1, i+1), YC_bnds[i,j,2] = 'northeast' corner (j+1, i+1), YC_bnds[i,j,3] = 'northwest' corner (j+1, i-1). Note: 'southwest', 'southeast', northwest', and 'northeast' do not correspond to geographic orientation but are used for convenience to describe the computational grid. See MITgcm dcoumentation for details.
- coverage_content_type :
- coordinate
- long_name :
- latitudes of tracer grid cell corners
[421200 values with dtype=float32]
- Z_bnds(k, nv)float32...
- comment :
- One pair of depths for each vertical level.
- coverage_content_type :
- coordinate
- long_name :
- depths of tracer grid cell upper and lower interfaces
array([[ 0. , -10. ], [ -10. , -20. ], [ -20. , -30. ], [ -30. , -40. ], [ -40. , -50. ], [ -50. , -60. ], [ -60. , -70. ], [ -70. , -80.01 ], [ -80.01 , -90.04 ], [ -90.04 , -100.15 ], [ -100.15 , -110.47 ], [ -110.47 , -121.270004], [ -121.270004, -133.03 ], [ -133.03 , -146.45 ], [ -146.45 , -162.48999 ], [ -162.48999 , -182.31 ], [ -182.31 , -207.16 ], [ -207.16 , -238.26001 ], [ -238.26001 , -276.68 ], [ -276.68 , -323.18 ], [ -323.18 , -378.18 ], [ -378.18 , -441.68 ], [ -441.68 , -513.26 ], [ -513.26 , -592.16003 ], [ -592.16003 , -677.31006 ], [ -677.31006 , -767.49005 ], [ -767.49005 , -861.4501 ], [ -861.4501 , -958.0301 ], [ -958.0301 , -1056.28 ], [-1056.28 , -1155.53 ], [-1155.53 , -1255.54 ], [-1255.54 , -1356.87 ], [-1356.87 , -1461.4299 ], [-1461.4299 , -1572.7599 ], [-1572.7599 , -1695.5898 ], [-1695.5898 , -1834.6798 ], [-1834.6798 , -1993.6199 ], [-1993.6199 , -2174.45 ], [-2174.45 , -2378. ], [-2378. , -2604.5 ], [-2604.5 , -2854. ], [-2854. , -3126.5 ], [-3126.5 , -3422. ], [-3422. , -3740.5 ], [-3740.5 , -4082. ], [-4082. , -4446.5 ], [-4446.5 , -4834. ], [-4834. , -5244.5 ], [-5244.5 , -5678. ], [-5678. , -6134.5 ]], dtype=float32)
- THETA(time, k, tile, j, i)float32...
- long_name :
- Potential temperature
- units :
- degree_C
- coverage_content_type :
- modelResult
- standard_name :
- sea_water_potential_temperature
- comment :
- Sea water potential temperature is the temperature a parcel of sea water would have if moved adiabatically to sea level pressure. Note: the equation of state is a modified UNESCO formula by Jackett and McDougall (1995), which uses the model variable potential temperature as input assuming a horizontally and temporally constant pressure of $p_0=-g \rho_{0} z$.
- valid_min :
- -2.2909388542175293
- valid_max :
- 36.032955169677734
[5265000 values with dtype=float32]
- SALT(time, k, tile, j, i)float32...
- long_name :
- Salinity
- units :
- 1e-3
- coverage_content_type :
- modelResult
- standard_name :
- sea_water_salinity
- valid_min :
- 17.106637954711914
- valid_max :
- 41.26802444458008
- comment :
- Defined using CF convention 'Sea water salinity is the salt content of sea water, often on the Practical Salinity Scale of 1978. However, the unqualified term 'salinity' is generic and does not necessarily imply any particular method of calculation. The units of salinity are dimensionless and the units attribute should normally be given as 1e-3 or 0.001 i.e. parts per thousand.' see https://cfconventions.org/Data/cf-standard-names/73/build/cf-standard-name-table.html
[5265000 values with dtype=float32]
- acknowledgement :
- This research was carried out by the Jet Propulsion Laboratory, managed by the California Institute of Technology under a contract with the National Aeronautics and Space Administration.
- author :
- Ian Fenty and Ou Wang
- cdm_data_type :
- Grid
- comment :
- Fields provided on the curvilinear lat-lon-cap 90 (llc90) native grid used in the ECCO model.
- Conventions :
- CF-1.8, ACDD-1.3
- coordinates_comment :
- Note: the global 'coordinates' attribute describes auxillary coordinates.
- creator_email :
- ecco-group@mit.edu
- creator_institution :
- NASA Jet Propulsion Laboratory (JPL)
- creator_name :
- ECCO Consortium
- creator_type :
- group
- creator_url :
- https://ecco-group.org
- date_created :
- 2020-12-18T14:40:01
- date_issued :
- 2020-12-18T14:40:01
- date_metadata_modified :
- 2021-03-15T21:54:34
- date_modified :
- 2021-03-15T21:54:34
- geospatial_bounds_crs :
- EPSG:4326
- geospatial_lat_max :
- 90.0
- geospatial_lat_min :
- -90.0
- geospatial_lat_resolution :
- variable
- geospatial_lat_units :
- degrees_north
- geospatial_lon_max :
- 180.0
- geospatial_lon_min :
- -180.0
- geospatial_lon_resolution :
- variable
- geospatial_lon_units :
- degrees_east
- geospatial_vertical_max :
- 0.0
- geospatial_vertical_min :
- -6134.5
- geospatial_vertical_positive :
- up
- geospatial_vertical_resolution :
- variable
- geospatial_vertical_units :
- meter
- history :
- Inaugural release of an ECCO Central Estimate solution to PO.DAAC
- id :
- 10.5067/ECL5M-OTS44
- institution :
- NASA Jet Propulsion Laboratory (JPL)
- instrument_vocabulary :
- GCMD instrument keywords
- keywords :
- EARTH SCIENCE SERVICES > MODELS > EARTH SCIENCE REANALYSES/ASSIMILATION MODELS, EARTH SCIENCE > OCEANS > OCEAN TEMPERATURE > POTENTIAL TEMPERATURE, EARTH SCIENCE > OCEANS > SALINITY/DENSITY > SALINITY
- keywords_vocabulary :
- NASA Global Change Master Directory (GCMD) Science Keywords
- license :
- Public Domain
- metadata_link :
- https://cmr.earthdata.nasa.gov/search/collections.umm_json?ShortName=ECCO_L4_TEMP_SALINITY_LLC0090GRID_MONTHLY_V4R4
- naming_authority :
- gov.nasa.jpl
- platform :
- ERS-1/2, TOPEX/Poseidon, Geosat Follow-On (GFO), ENVISAT, Jason-1, Jason-2, CryoSat-2, SARAL/AltiKa, Jason-3, AVHRR, Aquarius, SSM/I, SSMIS, GRACE, DTU17MDT, Argo, WOCE, GO-SHIP, MEOP, Ice Tethered Profilers (ITP)
- platform_vocabulary :
- GCMD platform keywords
- processing_level :
- L4
- product_name :
- OCEAN_TEMPERATURE_SALINITY_mon_mean_2000-01_ECCO_V4r4_native_llc0090.nc
- product_time_coverage_end :
- 2018-01-01T00:00:00
- product_time_coverage_start :
- 1992-01-01T12:00:00
- product_version :
- Version 4, Release 4
- program :
- NASA Physical Oceanography, Cryosphere, Modeling, Analysis, and Prediction (MAP)
- project :
- Estimating the Circulation and Climate of the Ocean (ECCO)
- publisher_email :
- podaac@podaac.jpl.nasa.gov
- publisher_institution :
- PO.DAAC
- publisher_name :
- Physical Oceanography Distributed Active Archive Center (PO.DAAC)
- publisher_type :
- institution
- publisher_url :
- https://podaac.jpl.nasa.gov
- references :
- ECCO Consortium, Fukumori, I., Wang, O., Fenty, I., Forget, G., Heimbach, P., & Ponte, R. M. 2020. Synopsis of the ECCO Central Production Global Ocean and Sea-Ice State Estimate (Version 4 Release 4). doi:10.5281/zenodo.3765928
- source :
- The ECCO V4r4 state estimate was produced by fitting a free-running solution of the MITgcm (checkpoint 66g) to satellite and in situ observational data in a least squares sense using the adjoint method
- standard_name_vocabulary :
- NetCDF Climate and Forecast (CF) Metadata Convention
- summary :
- This dataset provides monthly-averaged ocean potential temperature and salinity on the lat-lon-cap 90 (llc90) native model grid from the ECCO Version 4 Release 4 (V4r4) ocean and sea-ice state estimate. Estimating the Circulation and Climate of the Ocean (ECCO) state estimates are dynamically and kinematically-consistent reconstructions of the three-dimensional, time-evolving ocean, sea-ice, and surface atmospheric states. ECCO V4r4 is a free-running solution of a global, nominally 1-degree configuration of the MIT general circulation model (MITgcm) that has been fit to observations in a least-squares sense. Observational data constraints used in V4r4 include sea surface height (SSH) from satellite altimeters [ERS-1/2, TOPEX/Poseidon, GFO, ENVISAT, Jason-1,2,3, CryoSat-2, and SARAL/AltiKa]; sea surface temperature (SST) from satellite radiometers [AVHRR], sea surface salinity (SSS) from the Aquarius satellite radiometer/scatterometer, ocean bottom pressure (OBP) from the GRACE satellite gravimeter; sea-ice concentration from satellite radiometers [SSM/I and SSMIS], and in-situ ocean temperature and salinity measured with conductivity-temperature-depth (CTD) sensors and expendable bathythermographs (XBTs) from several programs [e.g., WOCE, GO-SHIP, Argo, and others] and platforms [e.g., research vessels, gliders, moorings, ice-tethered profilers, and instrumented pinnipeds]. V4r4 covers the period 1992-01-01T12:00:00 to 2018-01-01T00:00:00.
- time_coverage_duration :
- P1M
- time_coverage_end :
- 2000-02-01T00:00:00
- time_coverage_resolution :
- P1M
- time_coverage_start :
- 2000-01-01T00:00:00
- title :
- ECCO Ocean Temperature and Salinity - Monthly Mean llc90 Grid (Version 4 Release 4)
- uuid :
- f72f9b24-4181-11eb-bf7d-0cc47a3f4871
There are two data variables:
Potential temperature
THETA
in degrees Celsius, which according to the comment is the temperature a parcel of water would have if moved adiabatically to sea level pressure.Salinity
SALT
, which is the salt content of sea water in parts per thousand (units 1e-3, corresponding to g/kg).
Compute steric height anomaly¶
Now we will compute steric height anomaly from the density and pressure fields in the ECCOv4r4 output. Note that a function that computes steric height anomaly is already part of the GSW toolbox for Matlab and some other programming languages, but has not yet been implemented in the GSW-Python module. That’s OK, because it is fairly straightforward to implement this function in Python ourselves, especially when ECCO v4r4 already provides density and pressure as outputs. We can use GSW-Python to compute the reference specific volume , using GSW’s definition of as the specific volume at a given pressure level with a conservative temperature of 0 deg C and absolute salinity of 35.16504 g/kg.
Specific volume anomaly¶
[5]:
g = 9.81
rhoConst = 1029.
p_atm = 101325. # atmospheric pressure at sea surface, in Pa
press = (ds_denspress.PHIHYDcR + (g*(-ds_grid.Z)))*rhoConst # pressure
press_sea = press - p_atm # pressure minus mean atmospheric pressure
dens = ds_denspress.RHOAnoma + rhoConst # in-situ density
# compute standard specific volume and anomalies
S_Ar = 35.16504 # absolute salinity standard for spec. vol., notated as SSO in GSW documentation
T_Cr = 0. # conservative temperature standard
specvol_standard = gsw.density.specvol(S_Ar,T_Cr,(1.e-4)*press_sea.values)
specvol_anom = 1/dens - specvol_standard
[6]:
# # plot global tiles map of specific volume anomaly in topmost layer (k=0)
# k index (depth level) to plot
k_plot = 0
# 13 tiles map
curr_obj = ecco.plot_tiles(specvol_anom.isel(k=k_plot).squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=10,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10)
curr_fig = curr_obj[0]
curr_fig.suptitle('Specific volume anomaly at surface')
plt.show()
Notice that surface specific volume anomalies are largest in equatorial regions, particularly in the Indo-Pacific warm pool where surface waters are both very warm and relatively fresh (and therefore have relatively low density). Notice also that the specific volume anomaly is positive nearly everywhere, due to how the anomaly has been defined—based on water of temperature 0 C, at a salinity that is typical of ocean water but saltier than most water that cold.
Steric height anomaly¶
Now we’ll vertically integrate in pressure coordinates to obtain the steric height anomaly, relative to some reference pressure. In order to compute the integral in pressure coordinates, the pressure values are first computed at the top and bottom interfaces of each grid cell; then the integral is summed over grid cells above the defined reference pressure level.
[7]:
# pressure reference level to compute steric height
# (in units of dbar, minus 10.1325 dbar atmospheric pressure)
p_r_sea_dbar = 2000.
# # integrate vertically in pressure coordinates
p_r = ((1.e4)*p_r_sea_dbar) + p_atm
# compute pressure at z = 0 (not exactly the ocean surface)
press_z0 = press.isel(k=[0]) - (0.5*dens.isel(k=[0])*g*ds_grid.drF.isel(k=[0]))
press_z0.Z.values = np.array([ds_grid.Zl[0].values,])
# integrate hydrostatic balance downward to get pressure at bottom of grid cells
press_ku = press_z0.values + (dens*g*ds_grid.drF).cumsum("k")
press_ku.Z.values = ds_grid.Zu.values
# create array with pressure at top of grid cells
press_kl = xr.concat([press_z0,press_ku.isel(k=np.arange(len(ds_grid.k) - 1))],dim="k")
press_kl = press_kl.assign_coords(k=ds_grid.k.values)
Now let’s choose sea level pressure (i.e., sea pressure = 0 dbar) as the top limit of our integration, carry out the integration between these two levels, and plot the resulting map.
[8]:
# # compute steric height anomaly at given pressure p_top_sea_dbar, relative to p_r_sea_dbar
p_top_sea_dbar = 0.
p_top = ((1.e4)*p_top_sea_dbar) + p_atm
# compute dp for this integration
dp_integrate = np.fmax(press_kl,p_top*np.ones(press_kl.shape)) - \
np.fmin(press_ku,p_r*np.ones(press_ku.shape))
# allow integration above z=0 if p_top is less than p at z=0
p_top_above_z0_mask = (p_top - press_kl.isel(k=0).values < 0)
dp_integrate.isel(k=0).values[p_top_above_z0_mask] = \
(p_top - press_ku[:,0,:,:,:].values)[p_top_above_z0_mask]
dp_integrate.values[dp_integrate.values > 0] = 0
steric_hgt_anom = (-(specvol_anom/g)*dp_integrate).sum("k")
# # plot global tiles map of steric height at sea level pressure, i.e. sea pressure = 0 dbar
# # relative to p_r_sea_dbar
# 13 tiles map
curr_obj = ecco.plot_tiles(steric_hgt_anom.squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=10,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10)
curr_fig = curr_obj[0]
curr_fig.suptitle('Steric height anomaly [m] at ' + str(int(p_top_sea_dbar)) + ' dbar,\n'\
+ 'relative to ' + str(int(p_r_sea_dbar)) + ' dbar')
plt.show()
You can certainly make a few observations from the above map; notice the low steric height anomaly values in the high-salinity Mediterranean Sea for example. But there is a potential problem with the steric height anomaly integration, evident in the abrupt discontinuities across continental slopes (look closely at the areas near the Antarctic Peninsula or Argentina). When the bathymetry is shallower than the reference pressure level, this integration has assumed that the land areas are actually water parcels with the standard specific volume.
To avoid potentially misleading results, let’s mask out all areas where the bathymetry is shallower than the pressure level. We’ll plot this mask in gray along with a surface land mask in black, and for good measure we will rescale the steric height anomaly so that the global mean of the unmasked areas is removed.
[9]:
# load land mask (a boolean array) from grid parameters
land_mask = ~ds_grid.maskC
# surface land mask
land_mask_surf = land_mask.isel(k=0)
# mask out areas where sea pressure never reaches the reference pressure
too_shallow_mask = (press_ku.isel(k=-1) < p_r)
# steric height anomaly global mean, excluding masked areas
unmasked = (~land_mask_surf)*(~too_shallow_mask)
steric_hgt_globmean = np.sum(unmasked*ds_grid.rA*steric_hgt_anom)\
/np.sum(unmasked*ds_grid.rA)
steric_hgt_minus_globmean = steric_hgt_anom - steric_hgt_globmean
# function to plot a mask in ECCO tiles plots
def plot_mask_ecco_tiles(mask,color):
"""
Plot mask in global ECCO tiles plot on current axes,
given 2-D mask (xarray DataArray) and color (a string, RGB tuple or 3-element NumPy array).
"""
# loop through tiles to add mask
tile_order = np.array([-1,-1,-1,6, \
2,5,7,10, \
1,4,8,11, \
0,3,9,12])
for idx,curr_ax in enumerate(curr_fig.get_axes()):
if len(curr_ax.get_images()) > 0:
# plot land mask
array_plot = mask.isel(tile=tile_order[idx]).squeeze()
if tile_order[idx] == 6:
array_plot = np.rot90(array_plot,2)
elif tile_order[idx] > 6:
array_plot = np.rot90(array_plot)
plot_mask(array_plot,ax=curr_ax,color=color)
# 13 tiles map
curr_obj = ecco.plot_tiles(steric_hgt_minus_globmean.squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=10,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
plot_mask_ecco_tiles(too_shallow_mask,np.array([0.5,0.5,0.5])) # plot too-shallow mask
# add title
curr_fig.suptitle('Steric height minus global mean [m] at ' + str(int(p_top_sea_dbar)) + ' dbar,\n'\
+ 'relative to ' + str(int(p_r_sea_dbar)) + ' dbar')
plt.show()
Compare steric height with sea surface height¶
Of the 4 data variables in the SSH dataset, 3 are forms of sea surface height, just with different corrections applied. The SSH
variable is the dynamic sea surface height anomaly. Importantly, it includes the correction for global mean steric effects, often called the Greatbatch correction; this is essential for tracking steric impacts on sea level over time. The inverted barometer (IB) correction is for the effect of atmospheric pressure on sea level; here we will use the SSH
variable
which has the IB correction, and best reflects changes in sea level due to ocean mass and density.
[10]:
# remove global mean from SSH
SSH_globmean = np.sum((ds_grid.maskC.isel(k=0))*(ds_grid.rA)*ds_SSH.SSH)\
/np.sum((ds_grid.maskC.isel(k=0))*(ds_grid.rA))
SSH_minus_globmean = ds_SSH.SSH - SSH_globmean
# plot steric height and SSH minus respective global means
fig = plt.figure(constrained_layout=True)
subfigs = fig.subfigures(2,1)
curr_fig = subfigs[0]
# 13 tiles map
curr_obj = ecco.plot_tiles(steric_hgt_minus_globmean.squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=10,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10,\
cmin=-2.,cmax=2.)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
plot_mask_ecco_tiles(too_shallow_mask,np.array([0.5,0.5,0.5])) # plot too-shallow mask
curr_fig.suptitle('Steric height minus global mean [m] at ' + str(int(p_top_sea_dbar)) + ' dbar,\n'\
+ 'relative to ' + str(int(p_r_sea_dbar)) + ' dbar')
curr_fig = subfigs[1]
# 13 tiles map
curr_obj = ecco.plot_tiles(SSH_minus_globmean.squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=10,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10,\
cmin=-2.,cmax=2.)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
plot_mask_ecco_tiles(too_shallow_mask,np.array([0.5,0.5,0.5])) # plot too-shallow mask
curr_fig.suptitle('Sea surface height minus global mean [m],\n' + \
'with bathymetry shallower than ' + str(int(p_r_sea_dbar)) + ' dbar masked')
plt.show()
<Figure size 640x480 with 0 Axes>
Notice how similar these maps are, even though steric height is only one of the two primary components of sea level variation, and that the steric height map only includes the effect of density variations above 2000 dbar. A little more insight can be gained by taking the difference between the maps above to look at the non-steric height variation:
[11]:
nonsteric_hgt_minus_globmean = SSH_minus_globmean - steric_hgt_minus_globmean
# 13 tiles map
curr_obj = ecco.plot_tiles(nonsteric_hgt_minus_globmean.squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=10,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10,\
cmin=-2.,cmax=2.)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
plot_mask_ecco_tiles(too_shallow_mask,np.array([0.5,0.5,0.5])) # plot too-shallow mask
curr_fig.suptitle('Non-steric height minus global mean [m],\n' + \
'with bathymetry shallower than ' + str(int(p_r_sea_dbar)) + ' dbar masked')
plt.show()
The map above implies that non-steric effects (or steric effects deeper than 2000 dbar) on the regional variation of sea level are relatively small, with a few notable exceptions. For example, south of the Antarctic Circumpolar Current (ACC), SSH is much lower than might be expected from 0-2000 dbar steric height anomalies, suggesting that either (a) deep ocean steric anomalies must explain the discrepancy, or (b) a gradient in vertically-integrated ocean mass (i.e. deep ocean pressure) is sustained across the ACC. Which explanation do you think it is, and how might we test that hypothesis?
Thermosteric and halosteric contributions¶
Definitions and methods¶
In the thermodynamic equation of seawater or TEOS (also called the equation of state), density is a function of three parameters: absolute salinity, conservative temperature, and pressure. The pressure contribution is almost entirely a function of depth and is negligible when considering horizontal variations in density. Since density variations are a major influence on the ocean circulation and sea level, as we have seen in this tutorial, oceanographers are interested in the distinct contributions that temperature and salinity make to density. These contributions are called the thermosteric and halosteric contributions respectively.
For small variations in temperature or salinity , the relative contributions of each can be expressed as a linear approximation (e.g., Gill and Niiler 1973):
where we have defined a saline contraction coefficient and thermal expansion coefficient as the local derivatives of density with respect to salinity and temperature respectively. and are unitless coefficients, and they can also be expressed in terms of specific volume and . These linear approximations are particularly useful in computing the effect of temperature and salinity on density gradients in a small area; on global scales the nonlinearity of TEOS may lead to distortions using this approximation.
To illustrate this, let’s compute the thermosteric and halosteric contributions in two ways. In the first method, we will compute specific volume anomaly for each component using the linear approximations and around the reference specific volume (corresponding to a conservative temperature of = 0 deg C and absolute salinity of = 35.16504 g/kg).
where the subscript indicates values computed at the reference and .
In the second method, we will compute specific volume anomalies at each location while substituting in when computing the thermosteric contribution and when computing the halosteric contribution. In this way the specific volume anomaly is associated only with a difference in temperature or salinity respectively.
The difference between these two methods gives a sense of the impact of the nonlinearity of TEOS on the steric height anomaly.
Conservative temperature¶
The TEOS framework provides a number of different definitions of salinity, and it is a little ambiguous which of these corresponds most closely to the salinity field SALT
from the ECCOv4 output. However, absolute salinity is the salinity measure that directly impacts density, and since SALT
is used to compute density in the MITgcm, we will assume that SALT
corresponds to . For temperature, it is clear from
the variable’s comment that THETA
corresponds to potential temperature, while TEOS takes conservative temperature as an input. The difference between the two is not very large, but reflects the impact of salt concentrate on the thermal energy in seawater.
GSW-Python provides a number of conversion functions, including one to convert from potential temperature to conservative temperature.
[12]:
# look at syntax of potential -> conservative temperature conversion function
help(gsw.conversions.CT_from_pt)
Help on function CT_from_pt in module gsw._wrapped_ufuncs:
CT_from_pt(SA, pt)
Calculates Conservative Temperature of seawater from potential
temperature (whose reference sea pressure is zero dbar).
Parameters
----------
SA : array-like
Absolute Salinity, g/kg
pt : array-like
Potential temperature referenced to a sea pressure, degrees C
Returns
-------
CT : array-like, deg C
Conservative Temperature (ITS-90)
See that the function takes absolute salinity and potential temperature as inputs. Now let’s use the function, and plot the difference between potential and conservative temperature globally in the top (surface) layer. We’ll also plot a map of salinity in the surface layer, and you’ll see why.
[13]:
temp_pot = ds_TS.THETA
sal_abs = ds_TS.SALT
# compute conservative temperature
temp_cons = gsw.conversions.CT_from_pt(sal_abs,temp_pot)
# plot the difference between conservative and potential temperature at the surface
pot_minus_cons = temp_pot - temp_cons
k_plot = 0
# 13 tiles maps
fig = plt.figure(constrained_layout=True)
subfigs = fig.subfigures(2,1)
curr_fig = subfigs[0]
curr_obj = ecco.plot_tiles(pot_minus_cons.isel(k=k_plot).squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=10,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10,\
cmin=-0.14,cmax=0.14)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
curr_fig.suptitle('Potential temperature minus conservative temperature (deg C)\n' + \
'at depth ' + str(int(-ds_TS.Z.isel(k=k_plot))) + ' m')
curr_fig = subfigs[1]
curr_obj = ecco.plot_tiles(sal_abs.isel(k=k_plot).squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=10,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10,\
cmin=30.,cmax=40.)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
curr_fig.suptitle('Absolute salinity (g/kg) ' + \
'at depth ' + str(int(-ds_TS.Z.isel(k=k_plot))) + ' m')
plt.show()
<Figure size 640x480 with 0 Axes>
We know that conservative temperature can be computed from potential temperature and absolute salinity, so the consistency of the maps above is not a surprise. Still, it helps illustrate the effect of salinity on the temperature (vs. the heat content) of seawater.
Method 1: Using expansion/contraction coefficients¶
Now we will compute the thermosteric and halosteric contributions to steric height anomaly using the thermal expansion coefficient and the saline contraction coefficient , as linearized around the reference and . GSW-Python has functions to compute these coefficients.
[14]:
# thermal expansion coefficient alpha at T_Cr, S_Ar
alpha = gsw.density.alpha(S_Ar,T_Cr,(1.e-4)*press_sea.values)
# saline contraction coefficient beta at T_Cr, S_Ar
beta = gsw.density.beta(S_Ar,T_Cr,(1.e-4)*press_sea.values)
# thermosteric and halosteric specific volume anomalies
specvol_thermo_anom_linear = specvol_standard*alpha*(temp_cons - T_Cr)
specvol_halo_anom_linear = -specvol_standard*beta*(sal_abs - S_Ar)
# integrate vertically to obtain height anomalies
thermosteric_hgt_anom_linear = (-(specvol_thermo_anom_linear/g)*dp_integrate).sum("k")
halosteric_hgt_anom_linear = (-(specvol_halo_anom_linear/g)*dp_integrate).sum("k")
# contribution global means, excluding masked areas
thermosteric_hgt_linear_globmean = np.sum(unmasked*ds_grid.rA*thermosteric_hgt_anom_linear)\
/np.sum(unmasked*ds_grid.rA)
halosteric_hgt_linear_globmean = np.sum(unmasked*ds_grid.rA*halosteric_hgt_anom_linear)\
/np.sum(unmasked*ds_grid.rA)
# subtract global means
thermosteric_hgt_minus_globmean_linear = thermosteric_hgt_anom_linear \
- thermosteric_hgt_linear_globmean
halosteric_hgt_minus_globmean_linear = halosteric_hgt_anom_linear \
- halosteric_hgt_linear_globmean
# sum of two contributions
contribsum_hgt_minus_globmean_linear = thermosteric_hgt_minus_globmean_linear + \
halosteric_hgt_minus_globmean_linear
# plot thermosteric and halosteric contributions (linear approximations),
# plus their sum and the full steric height anomaly
fig = plt.figure(constrained_layout=True)
subfigs = fig.subfigures(2,1)
curr_fig = subfigs[0]
# 13 tiles map
curr_obj = ecco.plot_tiles(thermosteric_hgt_minus_globmean_linear.squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=6,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10,\
cmin=-2.,cmax=2.)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
plot_mask_ecco_tiles(too_shallow_mask,np.array([0.5,0.5,0.5])) # plot too-shallow mask
curr_fig.suptitle('Thermosteric height minus global mean [m] at ' + str(int(p_top_sea_dbar)) + ' dbar,\n'\
+ 'relative to ' + str(int(p_r_sea_dbar)) + ' dbar (computed using $\\alpha$)')
curr_fig = subfigs[1]
# 13 tiles map
curr_obj = ecco.plot_tiles(halosteric_hgt_minus_globmean_linear.squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=6,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10,\
cmin=-2.,cmax=2.)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
plot_mask_ecco_tiles(too_shallow_mask,np.array([0.5,0.5,0.5])) # plot too-shallow mask
curr_fig.suptitle('Halosteric height minus global mean [m] at ' + str(int(p_top_sea_dbar)) + ' dbar,\n'\
+ 'relative to ' + str(int(p_r_sea_dbar)) + ' dbar (computed using $\\beta$)')
plt.show()
<Figure size 640x480 with 0 Axes>
[15]:
# plot sum of contributions vs. full steric height anomaly
fig = plt.figure(constrained_layout=True)
subfigs = fig.subfigures(2,1)
curr_fig = subfigs[0]
# 13 tiles map
curr_obj = ecco.plot_tiles(contribsum_hgt_minus_globmean_linear.squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=6,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10,\
cmin=-2.,cmax=2.)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
plot_mask_ecco_tiles(too_shallow_mask,np.array([0.5,0.5,0.5])) # plot too-shallow mask
curr_fig.suptitle('Sum of linearized thermo+halo contributions [m] at ' + str(int(p_top_sea_dbar)) + ' dbar,\n'\
+ 'relative to ' + str(int(p_r_sea_dbar)) + ' dbar')
curr_fig = subfigs[1]
# 13 tiles map
curr_obj = ecco.plot_tiles(steric_hgt_minus_globmean.squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=6,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10,\
cmin=-2.,cmax=2.)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
plot_mask_ecco_tiles(too_shallow_mask,np.array([0.5,0.5,0.5])) # plot too-shallow mask
curr_fig.suptitle('Steric height minus global mean [m] at ' + str(int(p_top_sea_dbar)) + ' dbar,\n'\
+ 'relative to ' + str(int(p_r_sea_dbar)) + ' dbar')
plt.show()
<Figure size 640x480 with 0 Axes>
The first two maps show a thermosteric contribution that is largely latitude-dependent (no surprise), and a halosteric contribution that is very different in the Pacific (a fresher ocean) vs. the Atlantic and Indian oceans (more salty).
These patterns together help explain the full steric height variation in the last map above, though the sum of the two contributions obviously does not explain all of the full steric height variation.
Method 2: Using specific volume differences¶
Now let’s compute the thermosteric and halosteric contributions again, using the second method ( differences associated with temperature or salinity variations). This should account for at least some of the nonlinear aspects of TEOS.
[16]:
# compute specific volume anomalies due to temperature and salinity variations
specvol_thermo_anom = gsw.density.specvol(S_Ar,temp_cons,(1.e-4)*press_sea.values) - specvol_standard
specvol_halo_anom = gsw.density.specvol(sal_abs,T_Cr,(1.e-4)*press_sea.values) - specvol_standard
# integrate vertically to obtain height anomalies
thermosteric_hgt_anom = (-(specvol_thermo_anom/g)*dp_integrate).sum("k")
halosteric_hgt_anom = (-(specvol_halo_anom/g)*dp_integrate).sum("k")
# contribution global means, excluding masked areas
thermosteric_hgt_globmean = np.sum(unmasked*ds_grid.rA*thermosteric_hgt_anom)\
/np.sum(unmasked*ds_grid.rA)
halosteric_hgt_globmean = np.sum(unmasked*ds_grid.rA*halosteric_hgt_anom)\
/np.sum(unmasked*ds_grid.rA)
# subtract global means
thermosteric_hgt_minus_globmean = thermosteric_hgt_anom \
- thermosteric_hgt_globmean
halosteric_hgt_minus_globmean = halosteric_hgt_anom \
- halosteric_hgt_globmean
# sum of two contributions
contribsum_hgt_minus_globmean = thermosteric_hgt_minus_globmean + \
halosteric_hgt_minus_globmean
# plot thermosteric and halosteric contributions (linear approximations),
# plus their sum and the full steric height anomaly
fig = plt.figure(constrained_layout=True)
subfigs = fig.subfigures(2,1)
curr_fig = subfigs[0]
# 13 tiles map
curr_obj = ecco.plot_tiles(thermosteric_hgt_minus_globmean.squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=6,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10,\
cmin=-2.,cmax=2.)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
plot_mask_ecco_tiles(too_shallow_mask,np.array([0.5,0.5,0.5])) # plot too-shallow mask
curr_fig.suptitle('Thermosteric height minus global mean [m] at ' + str(int(p_top_sea_dbar)) + ' dbar,\n'\
+ 'relative to ' + str(int(p_r_sea_dbar)) + ' dbar')
curr_fig = subfigs[1]
# 13 tiles map
curr_obj = ecco.plot_tiles(halosteric_hgt_minus_globmean.squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=6,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10,\
cmin=-2.,cmax=2.)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
plot_mask_ecco_tiles(too_shallow_mask,np.array([0.5,0.5,0.5])) # plot too-shallow mask
curr_fig.suptitle('Halosteric height minus global mean [m] at ' + str(int(p_top_sea_dbar)) + ' dbar,\n'\
+ 'relative to ' + str(int(p_r_sea_dbar)) + ' dbar')
plt.show()
<Figure size 640x480 with 0 Axes>
And now let’s compare the sum of the contributions with the full steric height anomaly again.
[17]:
# plot sum of contributions vs. full steric height anomaly
fig = plt.figure(constrained_layout=True)
subfigs = fig.subfigures(2,1)
curr_fig = subfigs[0]
# 13 tiles map
curr_obj = ecco.plot_tiles(contribsum_hgt_minus_globmean.squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=6,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10,\
cmin=-2.,cmax=2.)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
plot_mask_ecco_tiles(too_shallow_mask,np.array([0.5,0.5,0.5])) # plot too-shallow mask
curr_fig.suptitle('Sum of thermo+halo contributions [m] at ' + str(int(p_top_sea_dbar)) + ' dbar,\n'\
+ 'relative to ' + str(int(p_r_sea_dbar)) + ' dbar')
curr_fig = subfigs[1]
# 13 tiles map
curr_obj = ecco.plot_tiles(steric_hgt_minus_globmean.squeeze(),\
cmap='seismic',show_colorbar=True,fig_size=6,\
layout='latlon',rotate_to_latlon=True,\
show_tile_labels=False,\
Arctic_cap_tile_location=10,\
cmin=-2.,cmax=2.)
curr_fig = curr_obj[0]
plot_mask_ecco_tiles(land_mask_surf,np.zeros(3,)) # plot land mask
plot_mask_ecco_tiles(too_shallow_mask,np.array([0.5,0.5,0.5])) # plot too-shallow mask
curr_fig.suptitle('Steric height minus global mean [m] at ' + str(int(p_top_sea_dbar)) + ' dbar,\n'\
+ 'relative to ' + str(int(p_r_sea_dbar)) + ' dbar')
plt.show()
<Figure size 640x480 with 0 Axes>
It looks like this time we did a much better job of accounting for the full steric height variation!
Exercises¶
Using the
lon_depth_along_lat
function defined in the thermal wind tutorial (and already imported here as part of theecco_py_tutorials
module), plot steric height anomalies and the thermosteric and halosteric contributions along a line of latitude around the globe. Since these anomalies have been vertically integrated, you may need to re-add thek
dimension to the DataArrays before using this function; you can use expand_dims:{array_name} = {array_name}.expand_dims(dim={“k”:ds_grid.Z.values[0]},axis=-4)
How do the relative contributions of temperature and salinity look different when following a line of latitude? You can also see how the reference pressure impacts the result by re-running this notebook with a different
p_r_sea_dbar
as input.Look at the change in steric height over time. Download the monthly density/pressure, SSH, and temperature/salinity files for each month in the year 1992, open the files using open_mfdataset, and average them in 1992 (the first year in the ECCOv4 output). Do the same for the year 2017 (the last year in the ECCOv4 output). Create maps similar to the ones in this tutorial, but for the (2017 mean) - (1992 mean). How did the steric, thermosteric, and halosteric contributions change over this 25-year period, and how do they compare to the change in dynamic SSH?
For this exercise you’ll download the density/pressure, SSH, and temperature/salinity files for the entire period 1992 through 2017. Plot time series of the global mean steric, thermosteric, and halosteric height alongside dynamic SSH. For extra credit, remove the seasonal cycle (by subtracting January means, February means, etc.) to make the longer time scale changes clearer.
References¶
Gill, A.E. (1982). Atmosphere-Ocean Dynamics. Academic Press, Elsevier.
Gill, A.E. & P.P. Niiler (1973). The theory of the seasonal variability in the ocean. Deep Sea Research and Oceanographic Abstracts, 20(2), 141-177. https://doi.org/10.1016/0011-7471(73)90049-1.
Vallis, G.K. (2006). Atmospheric and Oceanic Fluid Dynamics. Cambridge University Press.