Heat Transport¶
We will assume the heat budget in the model takes the form
The term on the LHS represents the advective flux of conservative temperature. The first term on the RHS is the convergence of diffusive fluxes of heat. In the model, this represents turbulent diffusion due to mesoscale eddies and small-scale mixing. The final term is the convergence of heat fluxes from the atmosphere.
We will further assume a rigid lid. Note that the ECCO model actually uses a sophisticated nonlinear free surface form of the conservation equations, so these approximations aren’t quite right. But our goal here is to focus on the big picture, and these details would be a distraction.
import xgcm
import xarray as xr
from matplotlib import pyplot as plt
import numpy as np
import hvplot.xarray
from ecco_utils import load_ecco_v4
plt.rcParams['figure.figsize'] = (12, 6)
ds = load_ecco_v4()
ds
<xarray.Dataset> Dimensions: (face: 13, lon_c: 360, lon_g: 360, lat_c: 240, lat_g: 240, Z: 50, Zl: 50, Zp1: 51, Zu: 50, time: 288, time_snp: 287) Coordinates: (12/42) * face (face) int64 0 1 2 3 4 5 6 7 8 9 10 11 12 i (lon_c) int64 0 1 2 3 4 5 6 7 ... 352 353 354 355 356 357 358 359 i_g (lon_g) int64 0 1 2 3 4 5 6 7 ... 352 353 354 355 356 357 358 359 j (lat_c) int64 30 31 32 33 34 35 36 ... 264 265 266 267 268 269 j_g (lat_g) int64 30 31 32 33 34 35 36 ... 264 265 266 267 268 269 k (Z) int64 0 1 2 3 4 5 6 7 8 9 ... 40 41 42 43 44 45 46 47 48 49 ... ... dyG (lat_c, lon_g) float32 dask.array<chunksize=(60, 90), meta=np.ndarray> dxG (lat_g, lon_c) float32 dask.array<chunksize=(60, 90), meta=np.ndarray> * lon_c (lon_c) float32 -37.5 -36.5 -35.5 -34.5 ... 319.5 320.5 321.5 * lon_g (lon_g) float32 -38.0 -37.0 -36.0 -35.0 ... 319.0 320.0 321.0 * lat_c (lat_c) float32 -81.46 -81.13 -80.8 -80.45 ... 67.16 67.34 67.47 * lat_g (lat_g) float32 -81.61 -81.28 -80.95 -80.61 ... 67.06 67.25 67.4 Data variables: (12/35) ADVr_SLT (time, Zl, lat_c, lon_c) float32 dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray> ADVr_TH (time, Zl, lat_c, lon_c) float32 dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray> DFrE_SLT (time, Zl, lat_c, lon_c) float32 dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray> DFrE_TH (time, Zl, lat_c, lon_c) float32 dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray> DFrI_SLT (time, Zl, lat_c, lon_c) float32 dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray> DFrI_TH (time, Zl, lat_c, lon_c) float32 dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray> ... ... UVELMASS (time, Z, lat_c, lon_g) float32 dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray> VVELMASS (time, Z, lat_g, lon_c) float32 dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray> UVELSTAR (time, Z, lat_c, lon_g) float32 dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray> VVELSTAR (time, Z, lat_g, lon_c) float32 dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray> oceTAUX (time, lat_c, lon_g) float32 dask.array<chunksize=(1, 60, 90), meta=np.ndarray> oceTAUY (time, lat_g, lon_c) float32 dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
- face: 13
- lon_c: 360
- lon_g: 360
- lat_c: 240
- lat_g: 240
- Z: 50
- Zl: 50
- Zp1: 51
- Zu: 50
- time: 288
- time_snp: 287
- face(face)int640 1 2 3 4 5 6 7 8 9 10 11 12
- standard_name :
- face_index
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
- i(lon_c)int640 1 2 3 4 5 ... 355 356 357 358 359
- axis :
- X
- long_name :
- x-dimension of the t grid
- standard_name :
- x_grid_index
- swap_dim :
- XC
array([ 0, 1, 2, ..., 357, 358, 359])
- i_g(lon_g)int640 1 2 3 4 5 ... 355 356 357 358 359
- axis :
- X
- c_grid_axis_shift :
- -0.5
- long_name :
- x-dimension of the u grid
- standard_name :
- x_grid_index_at_u_location
- swap_dim :
- XG
array([ 0, 1, 2, ..., 357, 358, 359])
- j(lat_c)int6430 31 32 33 34 ... 266 267 268 269
- axis :
- Y
- long_name :
- y-dimension of the t grid
- standard_name :
- y_grid_index
- swap_dim :
- YC
array([ 30, 31, 32, ..., 267, 268, 269])
- j_g(lat_g)int6430 31 32 33 34 ... 266 267 268 269
- axis :
- Y
- c_grid_axis_shift :
- -0.5
- long_name :
- y-dimension of the v grid
- standard_name :
- y_grid_index_at_v_location
- swap_dim :
- YG
array([ 30, 31, 32, ..., 267, 268, 269])
- k(Z)int640 1 2 3 4 5 6 ... 44 45 46 47 48 49
- axis :
- Z
- long_name :
- z-dimension of the t grid
- standard_name :
- z_grid_index
- swap_dim :
- Z
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(Zl)int640 1 2 3 4 5 6 ... 44 45 46 47 48 49
- axis :
- Z
- c_grid_axis_shift :
- -0.5
- long_name :
- z-dimension of the w grid
- standard_name :
- z_grid_index_at_upper_w_location
- swap_dim :
- Zl
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(Zp1)int640 1 2 3 4 5 6 ... 45 46 47 48 49 50
- axis :
- Z
- c_grid_axis_shift :
- [-0.5, 0.5]
- long_name :
- z-dimension of the w grid
- standard_name :
- z_grid_index_at_w_location
- swap_dim :
- Zp1
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])
- k_u(Zu)int640 1 2 3 4 5 6 ... 44 45 46 47 48 49
- axis :
- Z
- c_grid_axis_shift :
- 0.5
- long_name :
- z-dimension of the w grid
- standard_name :
- z_grid_index_at_lower_w_location
- swap_dim :
- Zu
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])
- time(time)datetime64[ns]1992-01-15 ... 2015-12-14
- axis :
- T
- long_name :
- Time
- standard_name :
- time
array(['1992-01-15T00:00:00.000000000', '1992-02-13T00:00:00.000000000', '1992-03-15T00:00:00.000000000', ..., '2015-10-15T00:00:00.000000000', '2015-11-14T00:00:00.000000000', '2015-12-14T00:00:00.000000000'], dtype='datetime64[ns]')
- time_snp(time_snp)datetime64[ns]1992-02-01 ... 2015-12-01
- axis :
- T
- c_grid_axis_shift :
- 0.5
- long_name :
- Time
- standard_name :
- time
array(['1992-02-01T00:00:00.000000000', '1992-03-01T00:00:00.000000000', '1992-04-01T00:00:00.000000000', ..., '2015-10-01T00:00:00.000000000', '2015-11-01T00:00:00.000000000', '2015-12-01T00:00:00.000000000'], dtype='datetime64[ns]')
- Depth(lat_c, lon_c)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
- coordinate :
- XC YC
- long_name :
- ocean depth
- standard_name :
- ocean_depth
- units :
- m
Array Chunk Bytes 337.50 kiB 31.64 kiB Shape (240, 360) (90, 90) Count 74 Tasks 12 Chunks Type float32 numpy.ndarray - PHrefC(Z)float32dask.array<chunksize=(50,), meta=np.ndarray>
- long_name :
- Reference Hydrostatic Pressure
- standard_name :
- cell_reference_pressure
- units :
- m2 s-2
Array Chunk Bytes 200 B 200 B Shape (50,) (50,) Count 2 Tasks 1 Chunks Type float32 numpy.ndarray - PHrefF(Zp1)float32dask.array<chunksize=(51,), meta=np.ndarray>
- long_name :
- Reference Hydrostatic Pressure
- standard_name :
- cell_reference_pressure
- units :
- m2 s-2
Array Chunk Bytes 204 B 204 B Shape (51,) (51,) Count 2 Tasks 1 Chunks Type float32 numpy.ndarray - XC(lat_c, lon_c)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
- coordinate :
- YC XC
- long_name :
- longitude
- standard_name :
- longitude
- units :
- degrees_east
Array Chunk Bytes 337.50 kiB 31.64 kiB Shape (240, 360) (90, 90) Count 74 Tasks 12 Chunks Type float32 numpy.ndarray - XG(lat_g, lon_g)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
- coordinate :
- YG XG
- long_name :
- longitude
- standard_name :
- longitude_at_f_location
- units :
- degrees_east
Array Chunk Bytes 337.50 kiB 31.64 kiB Shape (240, 360) (90, 90) Count 74 Tasks 12 Chunks Type float32 numpy.ndarray - YC(lat_c, lon_c)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
- coordinate :
- YC XC
- long_name :
- latitude
- standard_name :
- latitude
- units :
- degrees_north
Array Chunk Bytes 337.50 kiB 31.64 kiB Shape (240, 360) (90, 90) Count 74 Tasks 12 Chunks Type float32 numpy.ndarray - YG(lat_g, lon_g)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
- long_name :
- latitude
- standard_name :
- latitude_at_f_location
- units :
- degrees_north
Array Chunk Bytes 337.50 kiB 31.64 kiB Shape (240, 360) (90, 90) Count 74 Tasks 12 Chunks Type float32 numpy.ndarray - Z(Z)float32-5.0 -15.0 ... -5.906e+03
- long_name :
- vertical coordinate of cell center
- positive :
- down
- standard_name :
- depth
- units :
- m
- axis :
- Z
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)
- Zl(Zl)float320.0 -10.0 ... -5.244e+03 -5.678e+03
- long_name :
- vertical coordinate of upper cell interface
- positive :
- down
- standard_name :
- depth_at_upper_w_location
- units :
- m
- axis :
- Z
- c_grid_axis_shift :
- -0.5
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)
- Zp1(Zp1)float320.0 -10.0 ... -5.678e+03 -6.134e+03
- long_name :
- vertical coordinate of cell interface
- positive :
- down
- standard_name :
- depth_at_w_location
- units :
- m
- axis :
- Z
- c_grid_axis_shift :
- [-0.5, 0.5]
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(Zu)float32-10.0 -20.0 ... -6.134e+03
- long_name :
- vertical coordinate of lower cell interface
- positive :
- down
- standard_name :
- depth_at_lower_w_location
- units :
- m
- axis :
- Z
- c_grid_axis_shift :
- 0.5
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)
- basins(lat_c, lon_c)int16dask.array<chunksize=(60, 90), meta=np.ndarray>
Array Chunk Bytes 168.75 kiB 15.82 kiB Shape (240, 360) (90, 90) Count 86 Tasks 12 Chunks Type int16 numpy.ndarray - drC(Zp1)float32dask.array<chunksize=(51,), meta=np.ndarray>
- long_name :
- cell z size
- standard_name :
- cell_z_size_at_w_location
- units :
- m
Array Chunk Bytes 204 B 204 B Shape (51,) (51,) Count 2 Tasks 1 Chunks Type float32 numpy.ndarray - drF(Z)float32dask.array<chunksize=(50,), meta=np.ndarray>
- long_name :
- cell z size
- standard_name :
- cell_z_size
- units :
- m
Array Chunk Bytes 200 B 200 B Shape (50,) (50,) Count 2 Tasks 1 Chunks Type float32 numpy.ndarray - hFacC(Z, lat_c, lon_c)float32dask.array<chunksize=(50, 60, 90), meta=np.ndarray>
- long_name :
- vertical fraction of open cell
- standard_name :
- cell_vertical_fraction
Array Chunk Bytes 16.48 MiB 1.54 MiB Shape (50, 240, 360) (50, 90, 90) Count 74 Tasks 12 Chunks Type float32 numpy.ndarray - hFacS(Z, lat_g, lon_c)float32dask.array<chunksize=(50, 60, 90), meta=np.ndarray>
- long_name :
- vertical fraction of open cell
- standard_name :
- cell_vertical_fraction_at_v_location
Array Chunk Bytes 16.48 MiB 1.54 MiB Shape (50, 240, 360) (50, 90, 90) Count 74 Tasks 12 Chunks Type float32 numpy.ndarray - hFacW(Z, lat_c, lon_g)float32dask.array<chunksize=(50, 60, 90), meta=np.ndarray>
- long_name :
- vertical fraction of open cell
- standard_name :
- cell_vertical_fraction_at_u_location
Array Chunk Bytes 16.48 MiB 1.54 MiB Shape (50, 240, 360) (50, 90, 90) Count 74 Tasks 12 Chunks Type float32 numpy.ndarray - iter(time)int64dask.array<chunksize=(1,), meta=np.ndarray>
- long_name :
- model timestep number
- standard_name :
- timestep
Array Chunk Bytes 2.25 kiB 8 B Shape (288,) (1,) Count 289 Tasks 288 Chunks Type int64 numpy.ndarray - iter_snp(time_snp)int64dask.array<chunksize=(1,), meta=np.ndarray>
- long_name :
- model timestep number
- standard_name :
- timestep
Array Chunk Bytes 2.24 kiB 8 B Shape (287,) (1,) Count 288 Tasks 287 Chunks Type int64 numpy.ndarray - rA(lat_c, lon_c)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
- coordinate :
- YC XC
- long_name :
- cell area
- standard_name :
- cell_area
- units :
- m2
Array Chunk Bytes 337.50 kiB 31.64 kiB Shape (240, 360) (90, 90) Count 74 Tasks 12 Chunks Type float32 numpy.ndarray - rAs(lat_g, lon_c)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
- long_name :
- cell area
- standard_name :
- cell_area_at_v_location
- units :
- m2
Array Chunk Bytes 337.50 kiB 31.64 kiB Shape (240, 360) (90, 90) Count 74 Tasks 12 Chunks Type float32 numpy.ndarray - rAw(lat_c, lon_g)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
- coordinate :
- YG XC
- long_name :
- cell area
- standard_name :
- cell_area_at_u_location
- units :
- m2
Array Chunk Bytes 337.50 kiB 31.64 kiB Shape (240, 360) (90, 90) Count 74 Tasks 12 Chunks Type float32 numpy.ndarray - rAz(lat_g, lon_g)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
- coordinate :
- YG XG
- long_name :
- cell area
- standard_name :
- cell_area_at_f_location
- units :
- m
Array Chunk Bytes 337.50 kiB 31.64 kiB Shape (240, 360) (90, 90) Count 74 Tasks 12 Chunks Type float32 numpy.ndarray - dxC(lat_c, lon_g)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
- coordinate :
- YC XG
- long_name :
- cell x size
- standard_name :
- cell_x_size_at_u_location
- units :
- m
Array Chunk Bytes 337.50 kiB 31.64 kiB Shape (240, 360) (90, 90) Count 76 Tasks 12 Chunks Type float32 numpy.ndarray - dyC(lat_g, lon_c)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
- coordinate :
- YG XC
- long_name :
- cell y size
- standard_name :
- cell_y_size_at_v_location
- units :
- m
Array Chunk Bytes 337.50 kiB 31.29 kiB Shape (240, 360) (89, 90) Count 159 Tasks 20 Chunks Type float32 numpy.ndarray - dyG(lat_c, lon_g)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
- coordinate :
- YC XG
- long_name :
- cell y size
- standard_name :
- cell_y_size_at_u_location
- units :
- m
Array Chunk Bytes 337.50 kiB 31.64 kiB Shape (240, 360) (90, 90) Count 76 Tasks 12 Chunks Type float32 numpy.ndarray - dxG(lat_g, lon_c)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
- coordinate :
- YG XC
- long_name :
- cell x size
- standard_name :
- cell_x_size_at_v_location
- units :
- m
Array Chunk Bytes 337.50 kiB 31.29 kiB Shape (240, 360) (89, 90) Count 159 Tasks 20 Chunks Type float32 numpy.ndarray - lon_c(lon_c)float32-37.5 -36.5 -35.5 ... 320.5 321.5
- coordinate :
- YC XC
- long_name :
- longitude
- standard_name :
- longitude
- units :
- degrees_east
- axis :
- X
array([-37.5, -36.5, -35.5, ..., 319.5, 320.5, 321.5], dtype=float32)
- lon_g(lon_g)float32-38.0 -37.0 -36.0 ... 320.0 321.0
- coordinate :
- YG XG
- long_name :
- longitude
- standard_name :
- longitude_at_f_location
- units :
- degrees_east
- axis :
- X
- c_grid_axis_shift :
- -0.5
array([-38., -37., -36., ..., 319., 320., 321.], dtype=float32)
- lat_c(lat_c)float32-81.46 -81.13 -80.8 ... 67.34 67.47
- coordinate :
- YC XC
- long_name :
- latitude
- standard_name :
- latitude
- units :
- degrees_north
- axis :
- Y
array([-81.46223, -81.13084, -80.79505, ..., 67.16407, 67.33552, 67.47211], dtype=float32)
- lat_g(lat_g)float32-81.61 -81.28 -80.95 ... 67.25 67.4
- long_name :
- latitude
- standard_name :
- latitude_at_f_location
- units :
- degrees_north
- axis :
- Y
- c_grid_axis_shift :
- -0.5
array([-81.61184 , -81.283295, -80.95029 , ..., 67.06448 , 67.24895 , 67.40169 ], dtype=float32)
- ADVr_SLT(time, Zl, lat_c, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Vertical Advective Flux of Salinity
- standard_name :
- ADVr_SLT
- units :
- psu.m^3/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - ADVr_TH(time, Zl, lat_c, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Vertical Advective Flux of Pot.Temperature
- standard_name :
- ADVr_TH
- units :
- degC.m^3/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - DFrE_SLT(time, Zl, lat_c, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Vertical Diffusive Flux of Salinity (Explicit part)
- standard_name :
- DFrE_SLT
- units :
- psu.m^3/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - DFrE_TH(time, Zl, lat_c, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Vertical Diffusive Flux of Pot.Temperature (Explicit part)
- standard_name :
- DFrE_TH
- units :
- degC.m^3/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - DFrI_SLT(time, Zl, lat_c, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Vertical Diffusive Flux of Salinity (Implicit part)
- standard_name :
- DFrI_SLT
- units :
- psu.m^3/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - DFrI_TH(time, Zl, lat_c, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Vertical Diffusive Flux of Pot.Temperature (Implicit part)
- standard_name :
- DFrI_TH
- units :
- degC.m^3/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - ETAN(time, lat_c, lon_c)float32dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
- long_name :
- Surface Height Anomaly
- standard_name :
- ETAN
- units :
- m
Array Chunk Bytes 94.92 MiB 31.64 kiB Shape (288, 240, 360) (1, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - ETAN_snp(time_snp, lat_c, lon_c)float32dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
- long_name :
- Surface Height Anomaly
- standard_name :
- ETAN
- units :
- m
Array Chunk Bytes 94.59 MiB 31.64 kiB Shape (287, 240, 360) (1, 90, 90) Count 20952 Tasks 3444 Chunks Type float32 numpy.ndarray - GEOFLX(lat_c, lon_c)float32dask.array<chunksize=(60, 90), meta=np.ndarray>
Array Chunk Bytes 337.50 kiB 31.64 kiB Shape (240, 360) (90, 90) Count 75 Tasks 12 Chunks Type float32 numpy.ndarray - MXLDEPTH(time, lat_c, lon_c)float32dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
- coordinates :
- SN dt iter XC Depth YC CS rA
- long_name :
- Mixed-Layer Depth (>0)
- standard_name :
- MXLDEPTH
- units :
- m
Array Chunk Bytes 94.92 MiB 31.64 kiB Shape (288, 240, 360) (1, 90, 90) Count 24481 Tasks 3456 Chunks Type float32 numpy.ndarray - SALT(time, Z, lat_c, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Salinity
- standard_name :
- SALT
- units :
- psu
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - SALT_snp(time_snp, Z, lat_c, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Salinity
- standard_name :
- SALT
- units :
- psu
Array Chunk Bytes 4.62 GiB 1.54 MiB Shape (287, 50, 240, 360) (1, 50, 90, 90) Count 20952 Tasks 3444 Chunks Type float32 numpy.ndarray - SFLUX(time, lat_c, lon_c)float32dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
- long_name :
- total salt flux (match salt-content variations), >0 increases salt
- standard_name :
- SFLUX
- units :
- g/m^2/s
Array Chunk Bytes 94.92 MiB 31.64 kiB Shape (288, 240, 360) (1, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - TFLUX(time, lat_c, lon_c)float32dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
- long_name :
- total heat flux (match heat-content variations), >0 increases theta
- standard_name :
- TFLUX
- units :
- W/m^2
Array Chunk Bytes 94.92 MiB 31.64 kiB Shape (288, 240, 360) (1, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - THETA(time, Z, lat_c, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Potential Temperature
- standard_name :
- THETA
- units :
- degC
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - THETA_snp(time_snp, Z, lat_c, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Potential Temperature
- standard_name :
- THETA
- units :
- degC
Array Chunk Bytes 4.62 GiB 1.54 MiB Shape (287, 50, 240, 360) (1, 50, 90, 90) Count 20952 Tasks 3444 Chunks Type float32 numpy.ndarray - WVELMASS(time, Zl, lat_c, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Vertical Mass-Weighted Comp of Velocity
- standard_name :
- WVELMASS
- units :
- m/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - WVELSTAR(time, Zl, lat_c, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- coordinates :
- Zl SN dt iter XC Depth YC CS rA
- long_name :
- Vertical Component of Bolus Velocity
- standard_name :
- WVELSTAR
- units :
- m/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 24481 Tasks 3456 Chunks Type float32 numpy.ndarray - oceFWflx(time, lat_c, lon_c)float32dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
- long_name :
- net surface Fresh-Water flux into the ocean (+=down), >0 decreases salinity
- standard_name :
- oceFWflx
- units :
- kg/m^2/s
Array Chunk Bytes 94.92 MiB 31.64 kiB Shape (288, 240, 360) (1, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - oceQsw(time, lat_c, lon_c)float32dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
- long_name :
- net Short-Wave radiation (+=down), >0 increases theta
- standard_name :
- oceQsw
- units :
- W/m^2
Array Chunk Bytes 94.92 MiB 31.64 kiB Shape (288, 240, 360) (1, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - oceSPtnd(time, Z, lat_c, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- salt tendency due to salt plume flux >0 increases salinity
- standard_name :
- oceSPtnd
- units :
- g/m^2/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21025 Tasks 3456 Chunks Type float32 numpy.ndarray - ADVx_SLT(time, Z, lat_c, lon_g)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Zonal Advective Flux of Salinity
- mate :
- ADVy_SLT
- standard_name :
- ADVx_SLT
- units :
- psu.m^3/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21314 Tasks 3456 Chunks Type float32 numpy.ndarray - ADVy_SLT(time, Z, lat_g, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Meridional Advective Flux of Salinity
- mate :
- ADVx_SLT
- standard_name :
- ADVy_SLT
- units :
- psu.m^3/s
Array Chunk Bytes 4.63 GiB 1.53 MiB Shape (288, 50, 240, 360) (1, 50, 89, 90) Count 47522 Tasks 5760 Chunks Type float32 numpy.ndarray - ADVx_TH(time, Z, lat_c, lon_g)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Zonal Advective Flux of Pot.Temperature
- mate :
- ADVy_TH
- standard_name :
- ADVx_TH
- units :
- degC.m^3/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21314 Tasks 3456 Chunks Type float32 numpy.ndarray - ADVy_TH(time, Z, lat_g, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Meridional Advective Flux of Pot.Temperature
- mate :
- ADVx_TH
- standard_name :
- ADVy_TH
- units :
- degC.m^3/s
Array Chunk Bytes 4.63 GiB 1.53 MiB Shape (288, 50, 240, 360) (1, 50, 89, 90) Count 47522 Tasks 5760 Chunks Type float32 numpy.ndarray - DFxE_SLT(time, Z, lat_c, lon_g)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Zonal Diffusive Flux of Salinity
- mate :
- DFyE_SLT
- standard_name :
- DFxE_SLT
- units :
- psu.m^3/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21314 Tasks 3456 Chunks Type float32 numpy.ndarray - DFyE_SLT(time, Z, lat_g, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Meridional Diffusive Flux of Salinity
- mate :
- DFxE_SLT
- standard_name :
- DFyE_SLT
- units :
- psu.m^3/s
Array Chunk Bytes 4.63 GiB 1.53 MiB Shape (288, 50, 240, 360) (1, 50, 89, 90) Count 47522 Tasks 5760 Chunks Type float32 numpy.ndarray - DFxE_TH(time, Z, lat_c, lon_g)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Zonal Diffusive Flux of Pot.Temperature
- mate :
- DFyE_TH
- standard_name :
- DFxE_TH
- units :
- degC.m^3/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21314 Tasks 3456 Chunks Type float32 numpy.ndarray - DFyE_TH(time, Z, lat_g, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Meridional Diffusive Flux of Pot.Temperature
- mate :
- DFxE_TH
- standard_name :
- DFyE_TH
- units :
- degC.m^3/s
Array Chunk Bytes 4.63 GiB 1.53 MiB Shape (288, 50, 240, 360) (1, 50, 89, 90) Count 47522 Tasks 5760 Chunks Type float32 numpy.ndarray - UVELMASS(time, Z, lat_c, lon_g)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Zonal Mass-Weighted Comp of Velocity (m/s)
- mate :
- VVELMASS
- standard_name :
- UVELMASS
- units :
- m/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 21314 Tasks 3456 Chunks Type float32 numpy.ndarray - VVELMASS(time, Z, lat_g, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- long_name :
- Meridional Mass-Weighted Comp of Velocity (m/s)
- mate :
- UVELMASS
- standard_name :
- VVELMASS
- units :
- m/s
Array Chunk Bytes 4.63 GiB 1.53 MiB Shape (288, 50, 240, 360) (1, 50, 89, 90) Count 47522 Tasks 5760 Chunks Type float32 numpy.ndarray - UVELSTAR(time, Z, lat_c, lon_g)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- coordinates :
- hFacW dt PHrefC Z iter dxC drF rAw dyG
- long_name :
- Zonal Component of Bolus Velocity
- mate :
- VVELSTAR
- standard_name :
- UVELSTAR
- units :
- m/s
Array Chunk Bytes 4.63 GiB 1.54 MiB Shape (288, 50, 240, 360) (1, 50, 90, 90) Count 28226 Tasks 3456 Chunks Type float32 numpy.ndarray - VVELSTAR(time, Z, lat_g, lon_c)float32dask.array<chunksize=(1, 50, 60, 90), meta=np.ndarray>
- coordinates :
- dt PHrefC Z iter dxG rAs hFacS dyC drF
- long_name :
- Meridional Component of Bolus Velocity
- mate :
- UVELSTAR
- standard_name :
- VVELSTAR
- units :
- m/s
Array Chunk Bytes 4.63 GiB 1.53 MiB Shape (288, 50, 240, 360) (1, 50, 89, 90) Count 54434 Tasks 5760 Chunks Type float32 numpy.ndarray - oceTAUX(time, lat_c, lon_g)float32dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
- coordinates :
- dt iter dxC rAw dyG
- long_name :
- zonal surface wind stress, >0 increases uVel
- mate :
- oceTAUY
- standard_name :
- oceTAUX
- units :
- N/m^2
Array Chunk Bytes 94.92 MiB 31.64 kiB Shape (288, 240, 360) (1, 90, 90) Count 28226 Tasks 3456 Chunks Type float32 numpy.ndarray - oceTAUY(time, lat_g, lon_c)float32dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
- coordinates :
- dt dxG iter rAs dyC
- long_name :
- meridional surf. wind stress, >0 increases vVel
- mate :
- oceTAUX
- standard_name :
- oceTAUY
- units :
- N/m^2
Array Chunk Bytes 94.92 MiB 31.29 kiB Shape (288, 240, 360) (1, 89, 90) Count 54434 Tasks 5760 Chunks Type float32 numpy.ndarray
Vertically Integrated Heat Budget¶
Integrating the budget vertically and assuming \(\eta = 0\), we obtain
We can write this in a more compact form as
Where \(\mathbf{F}^\Theta_{adv,H}\) and \(\mathbf{F}^\Theta_{diff,H}\) are the vertically integrated advective and diffusive (2D) heat flux vectors. The operator \(\nabla_H\) represents the divergence in two dimensions (x, y).
from dask_gateway import Gateway
gway = Gateway()
cluster = gway.new_cluster()
cluster
/srv/conda/envs/notebook/lib/python3.8/site-packages/dask_gateway/client.py:21: FutureWarning: format_bytes is deprecated and will be removed in a future release. Please use dask.utils.format_bytes instead.
from distributed.utils import LoopRunner, format_bytes
grid = xgcm.Grid(ds, periodic=['X'])
fluxes = xr.Dataset({
'F_adv_x': ds.ADVx_TH.sum('Z'),
'F_adv_y': ds.ADVy_TH.sum('Z'),
'F_diff_x': ds.DFxE_TH.sum('Z'),
'F_diff_y': ds.DFyE_TH.sum('Z'),
}).reset_coords(drop=True)
fluxes
<xarray.Dataset> Dimensions: (lon_g: 360, lat_c: 240, time: 288, lon_c: 360, lat_g: 240) Coordinates: * time (time) datetime64[ns] 1992-01-15 1992-02-13 ... 2015-12-14 * lon_g (lon_g) float32 -38.0 -37.0 -36.0 -35.0 ... 319.0 320.0 321.0 * lat_c (lat_c) float32 -81.46 -81.13 -80.8 -80.45 ... 67.16 67.34 67.47 * lon_c (lon_c) float32 -37.5 -36.5 -35.5 -34.5 ... 319.5 320.5 321.5 * lat_g (lat_g) float32 -81.61 -81.28 -80.95 -80.61 ... 67.06 67.25 67.4 Data variables: F_adv_x (time, lat_c, lon_g) float32 dask.array<chunksize=(1, 60, 90), meta=np.ndarray> F_adv_y (time, lat_g, lon_c) float32 dask.array<chunksize=(1, 60, 90), meta=np.ndarray> F_diff_x (time, lat_c, lon_g) float32 dask.array<chunksize=(1, 60, 90), meta=np.ndarray> F_diff_y (time, lat_g, lon_c) float32 dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
- lon_g: 360
- lat_c: 240
- time: 288
- lon_c: 360
- lat_g: 240
- time(time)datetime64[ns]1992-01-15 ... 2015-12-14
- axis :
- T
- long_name :
- Time
- standard_name :
- time
array(['1992-01-15T00:00:00.000000000', '1992-02-13T00:00:00.000000000', '1992-03-15T00:00:00.000000000', ..., '2015-10-15T00:00:00.000000000', '2015-11-14T00:00:00.000000000', '2015-12-14T00:00:00.000000000'], dtype='datetime64[ns]')
- lon_g(lon_g)float32-38.0 -37.0 -36.0 ... 320.0 321.0
- coordinate :
- YG XG
- long_name :
- longitude
- standard_name :
- longitude_at_f_location
- units :
- degrees_east
- axis :
- X
- c_grid_axis_shift :
- -0.5
array([-38., -37., -36., ..., 319., 320., 321.], dtype=float32)
- lat_c(lat_c)float32-81.46 -81.13 -80.8 ... 67.34 67.47
- coordinate :
- YC XC
- long_name :
- latitude
- standard_name :
- latitude
- units :
- degrees_north
- axis :
- Y
array([-81.46223, -81.13084, -80.79505, ..., 67.16407, 67.33552, 67.47211], dtype=float32)
- lon_c(lon_c)float32-37.5 -36.5 -35.5 ... 320.5 321.5
- coordinate :
- YC XC
- long_name :
- longitude
- standard_name :
- longitude
- units :
- degrees_east
- axis :
- X
array([-37.5, -36.5, -35.5, ..., 319.5, 320.5, 321.5], dtype=float32)
- lat_g(lat_g)float32-81.61 -81.28 -80.95 ... 67.25 67.4
- long_name :
- latitude
- standard_name :
- latitude_at_f_location
- units :
- degrees_north
- axis :
- Y
- c_grid_axis_shift :
- -0.5
array([-81.61184 , -81.283295, -80.95029 , ..., 67.06448 , 67.24895 , 67.40169 ], dtype=float32)
- F_adv_x(time, lat_c, lon_g)float32dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
Array Chunk Bytes 94.92 MiB 31.64 kiB Shape (288, 240, 360) (1, 90, 90) Count 35138 Tasks 3456 Chunks Type float32 numpy.ndarray - F_adv_y(time, lat_g, lon_c)float32dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
Array Chunk Bytes 94.92 MiB 31.29 kiB Shape (288, 240, 360) (1, 89, 90) Count 70562 Tasks 5760 Chunks Type float32 numpy.ndarray - F_diff_x(time, lat_c, lon_g)float32dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
Array Chunk Bytes 94.92 MiB 31.64 kiB Shape (288, 240, 360) (1, 90, 90) Count 35138 Tasks 3456 Chunks Type float32 numpy.ndarray - F_diff_y(time, lat_g, lon_c)float32dask.array<chunksize=(1, 60, 90), meta=np.ndarray>
Array Chunk Bytes 94.92 MiB 31.29 kiB Shape (288, 240, 360) (1, 89, 90) Count 70562 Tasks 5760 Chunks Type float32 numpy.ndarray
try:
fluxes = xr.open_dataset('tmp_data/ECCO_lateral_heat_fluxes.nc')
except FileNotFoundError:
import dask
cluster.scale(20)
with cluster.get_client():
for vname in fluxes.data_vars:
fluxes[vname].load()
cluster.scale(0)
fluxes.to_netcdf('tmp_data/ECCO_lateral_heat_fluxes.nc')
fluxes
<xarray.Dataset> Dimensions: (time: 288, lon_g: 360, lat_c: 240, lon_c: 360, lat_g: 240) Coordinates: * time (time) datetime64[ns] 1992-01-15 1992-02-13 ... 2015-12-14 * lon_g (lon_g) float32 -38.0 -37.0 -36.0 -35.0 ... 319.0 320.0 321.0 * lat_c (lat_c) float32 -81.46 -81.13 -80.8 -80.45 ... 67.16 67.34 67.47 * lon_c (lon_c) float32 -37.5 -36.5 -35.5 -34.5 ... 319.5 320.5 321.5 * lat_g (lat_g) float32 -81.61 -81.28 -80.95 -80.61 ... 67.06 67.25 67.4 Data variables: F_adv_x (time, lat_c, lon_g) float32 ... F_adv_y (time, lat_g, lon_c) float32 ... F_diff_x (time, lat_c, lon_g) float32 ... F_diff_y (time, lat_g, lon_c) float32 ...
- time: 288
- lon_g: 360
- lat_c: 240
- lon_c: 360
- lat_g: 240
- time(time)datetime64[ns]1992-01-15 ... 2015-12-14
- axis :
- T
- long_name :
- Time
- standard_name :
- time
array(['1992-01-15T00:00:00.000000000', '1992-02-13T00:00:00.000000000', '1992-03-15T00:00:00.000000000', ..., '2015-10-15T00:00:00.000000000', '2015-11-14T00:00:00.000000000', '2015-12-14T00:00:00.000000000'], dtype='datetime64[ns]')
- lon_g(lon_g)float32-38.0 -37.0 -36.0 ... 320.0 321.0
- coordinate :
- YG XG
- long_name :
- longitude
- standard_name :
- longitude_at_f_location
- units :
- degrees_east
- axis :
- X
- c_grid_axis_shift :
- -0.5
array([-38., -37., -36., ..., 319., 320., 321.], dtype=float32)
- lat_c(lat_c)float32-81.46 -81.13 -80.8 ... 67.34 67.47
- coordinate :
- YC XC
- long_name :
- latitude
- standard_name :
- latitude
- units :
- degrees_north
- axis :
- Y
array([-81.46223, -81.13084, -80.79505, ..., 67.16407, 67.33552, 67.47211], dtype=float32)
- lon_c(lon_c)float32-37.5 -36.5 -35.5 ... 320.5 321.5
- coordinate :
- YC XC
- long_name :
- longitude
- standard_name :
- longitude
- units :
- degrees_east
- axis :
- X
array([-37.5, -36.5, -35.5, ..., 319.5, 320.5, 321.5], dtype=float32)
- lat_g(lat_g)float32-81.61 -81.28 -80.95 ... 67.25 67.4
- long_name :
- latitude
- standard_name :
- latitude_at_f_location
- units :
- degrees_north
- axis :
- Y
- c_grid_axis_shift :
- -0.5
array([-81.61184 , -81.283295, -80.95029 , ..., 67.06448 , 67.24895 , 67.40169 ], dtype=float32)
- F_adv_x(time, lat_c, lon_g)float32...
[24883200 values with dtype=float32]
- F_adv_y(time, lat_g, lon_c)float32...
[24883200 values with dtype=float32]
- F_diff_x(time, lat_c, lon_g)float32...
[24883200 values with dtype=float32]
- F_diff_y(time, lat_g, lon_c)float32...
[24883200 values with dtype=float32]
over_rA = (ds.rA.compute()**-1).fillna(0.)
advective_flux_convergence = -(
grid.diff(fluxes.F_adv_x, 'X', boundary='extend') +
grid.diff(fluxes.F_adv_y, 'Y', boundary='extend')
) * over_rA
diffusive_flux_convergence = -(
grid.diff(fluxes.F_diff_x, 'X', boundary='extend') +
grid.diff(fluxes.F_diff_y, 'Y', boundary='extend')
) * over_rA
rho0 = 1029.
cp = 3850.
land_mask = (ds.hFacC[0] > 0).compute()
surface_flux = ds.TFLUX.compute().where(land_mask) / (rho0 * cp)
clim = (-1e-4, 1e-4)
big_plot = (
advective_flux_convergence.hvplot.quadmesh('lon_c', 'lat_c', clim=clim, cmap='RdBu_r', label='Advective_Flux_Convergence') +
diffusive_flux_convergence.hvplot.quadmesh('lon_c', 'lat_c', clim=clim, cmap='RdBu_r', label='Meridional_Flux_Convergence') +
surface_flux.hvplot.quadmesh('lon_c', 'lat_c',clim=clim, cmap='RdBu_r', label='Surface_Flux')
).cols(1)
big_plot
In the long term mean, we see that the convergence of the lateral fluxes balances the net air-sea fluxes quite closely.
import cartopy.crs as ccrs
proj = ccrs.Robinson(central_longitude=160)
fig, axes = plt.subplots(ncols=2, figsize=(16, 5), subplot_kw=dict(projection=proj, facecolor='0.9'))
for ax in axes:
ax.coastlines()
clim = (-2e-5, 2e-5)
pc1 = surface_flux.mean('time').plot(ax=axes[0], transform=ccrs.PlateCarree(), add_colorbar=False)
pc2 = (advective_flux_convergence + diffusive_flux_convergence).mean('time').plot(ax=axes[1], transform=ccrs.PlateCarree(), add_colorbar=False)
pc1.set_clim(clim)
pc2.set_clim(clim)
axes[0].set_title('Time Mean Surface Flux')
axes[1].set_title('Time Mean Heat Transport Convergence')
plt.close()
fig
Ocean Meridional Heat Transport¶
If we integrate one more time in the \(x\) (i.e. Longitude) direction, around closed latitude circules we obtain a heat budget that is a function of latitude and time only.
The first term RHS is the Ocean Meridional Heat Transport, a fundamental quantity in the climate system.
F_merid_adv = (rho0 * cp * fluxes.F_adv_y.sum('lon_c') / 1e15)
F_merid_diff = (rho0 * cp * fluxes.F_diff_y.sum('lon_c') / 1e15)
F_merid_tot = (F_merid_adv + F_merid_diff)
F_merid_tot.mean('time', keep_attrs=True).plot(color='black', label='Meridional Heat Transport')
F_merid_adv.mean('time', keep_attrs=True).plot(label='Advective Meridional Heat Transport')
F_merid_diff.mean('time', keep_attrs=True).plot(label='Diffusive Meridional Heat Transport')
plt.legend()
plt.grid()
plt.ylabel('PW')
Text(0, 0.5, 'PW')
Separating Atlatic and Indo-Pacific¶
By integrating over basins separately, we reveal the very distinct patterns of meridional heat transport in the Atlantic vs. Indo-Pacific.
atl_basins = [2, 7, 9, 10, 12, 19]
atl_mask = sum([ds.basins == basin_num for basin_num in atl_basins])
mask_c = atl_mask.compute()
mask_v = (grid.interp(mask_c, 'Y', boundary='fill') > 0)
F_merid_atl = rho0 * cp * (fluxes.F_diff_y + fluxes.F_adv_y).where(mask_v).sum('lon_c').sel(lat_g=slice(-30, None)) / 1e15
F_merid_rest = rho0 * cp * (fluxes.F_diff_y + fluxes.F_adv_y).where(~mask_v).sum('lon_c').sel(lat_g=slice(-30, None)) / 1e15
F_merid_tot.mean('time').plot(color='black', label='Meridional Heat Transport')
F_merid_adv.mean('time').plot(label='Advective Meridional Heat Transport', color='grey')
F_merid_atl.mean('time').plot(label='Atlantic', color='red')
F_merid_rest.mean('time').plot(label='Indo Pacific', color='blue')
plt.legend()
plt.grid()
plt.ylabel('PW')
Text(0, 0.5, 'PW')
In this view, its very clear that:
The Atlantic transports heat northwards at all latitudes, even in the Southern Hemisphere!
The Pacific must compensate for this in order to achieve a net southward heat transport in the Southen Hemisphere.
Decomposing the Advective Meridional Heat Transport¶
What physical circulation processes are responsible for meridional heat transport? We will partially answer this question by decomposing our heat transport into a component that can be attributed directly to the meridional overturning and a residual, representing all other elements of the flow.
The net advective meridional heat transport can be written as:
Now let’s define the zonal mean velocity and temperature as
where \(L\) is the length of the latitude circle.
The total velocity and temperature can be written as a sum of this mean component and a fluctuation from it.
Using this definition, we can write the advective transport in terms of two separate contributions.
The quantity \(L \oint \overline{v} dx = \oint v dx \) can be defined in terms of the overturning streamfunction \(\Psi\) (see Overturning Circulation). This allows us to write
Using integration by parts, together with the fact that \(\Psi = 0\) at \(z=0, -H\) allows us to rewrite the first term as.
The first term represents the overturning circulation acting on the vertical temperature gradient. It is similar in many ways to the two-layer model of heat transport we saw in Advection, Diffusion, and Conservation Laws. The heat transport by the overturning circulation is proportional two two main factors:
The strength of the overturning \(\Psi\)
The mean ocean thermal stratification \(\partial \overline{\Theta} / \partial z\)
ds_theta_bar = xr.Dataset({
'Theta_bar': ds.THETA.where(ds.hFacC>0).mean('lon_c'),
'Theta_bar_atl': ds.THETA.where(mask_c).where(ds.hFacC>0).mean('lon_c'),
'Theta_bar_rest': ds.THETA.where(~mask_c).where(ds.hFacC>0).mean('lon_c')
}).reset_coords(drop=True)
try:
ds_theta_bar = xr.open_dataset("tmp_data/ECCO_Theta_bar.nc")
except FileNotFoundError:
cluster.scale(20)
with cluster.get_client():
ds_theta_bar.load()
cluster.scale(0)
ds_theta_bar.to_netcdf("tmp_data/ECCO_Theta_bar.nc")
ds_transport = xr.open_dataset('tmp_data/ECCO_overturning_transport.nc')
ds_transport
<xarray.Dataset> Dimensions: (time: 288, Z: 50, lat_g: 240) Coordinates: * time (time) datetime64[ns] 1992-01-15 1992-02-13 ... 2015-12-14 * Z (Z) float32 -5.0 -15.0 -25.0 ... -5.461e+03 -5.906e+03 * lat_g (lat_g) float32 -81.61 -81.28 -80.95 ... 67.06 67.25 67.4 Data variables: V_atl (time, Z, lat_g) float32 ... V_rest (time, Z, lat_g) float32 ... V_eddy_atl (time, Z, lat_g) float32 ... V_eddy_rest (time, Z, lat_g) float32 ...
- time: 288
- Z: 50
- lat_g: 240
- time(time)datetime64[ns]1992-01-15 ... 2015-12-14
- axis :
- T
- long_name :
- Time
- standard_name :
- time
array(['1992-01-15T00:00:00.000000000', '1992-02-13T00:00:00.000000000', '1992-03-15T00:00:00.000000000', ..., '2015-10-15T00:00:00.000000000', '2015-11-14T00:00:00.000000000', '2015-12-14T00:00:00.000000000'], dtype='datetime64[ns]')
- Z(Z)float32-5.0 -15.0 ... -5.906e+03
- long_name :
- vertical coordinate of cell center
- positive :
- down
- standard_name :
- depth
- units :
- m
- axis :
- Z
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)
- lat_g(lat_g)float32-81.61 -81.28 -80.95 ... 67.25 67.4
- long_name :
- latitude
- standard_name :
- latitude_at_f_location
- units :
- degrees_north
- axis :
- Y
- c_grid_axis_shift :
- -0.5
array([-81.61184 , -81.283295, -80.95029 , ..., 67.06448 , 67.24895 , 67.40169 ], dtype=float32)
- V_atl(time, Z, lat_g)float32...
[3456000 values with dtype=float32]
- V_rest(time, Z, lat_g)float32...
[3456000 values with dtype=float32]
- V_eddy_atl(time, Z, lat_g)float32...
[3456000 values with dtype=float32]
- V_eddy_rest(time, Z, lat_g)float32...
[3456000 values with dtype=float32]
Theta_bar_g = grid.interp(ds_theta_bar.Theta_bar, 'Y', boundary='extend')
Theta_bar_atl_g = grid.interp(ds_theta_bar.Theta_bar_atl, 'Y', boundary='extend')
Theta_bar_rest_g = grid.interp(ds_theta_bar.Theta_bar_rest, 'Y', boundary='extend')
F_adv_MOC = rho0 * cp * (Theta_bar_g * (ds_transport.V_atl + ds_transport.V_rest)).sum('Z') / 1e15
F_adv_MOC_atl = rho0 * cp * (Theta_bar_atl_g * ds_transport.V_atl).sum('Z').sel(lat_g=slice(-30, None)) / 1e15
F_adv_MOC_rest = rho0 * cp * (Theta_bar_rest_g * ds_transport.V_rest).sum('Z').sel(lat_g=slice(-30, None)) / 1e15
F_merid_tot.mean('time').plot(color='black', label='Meridional Heat Transport')
F_merid_adv.mean('time').plot(label='Advective Meridional Heat Transport', color='grey')
F_merid_atl.mean('time').plot(label='Atlantic', color='r')
F_merid_rest.mean('time').plot(label='Indo Pacific', color='b')
F_adv_MOC.mean('time').plot(label='Overturning Meridional Heat Transport', color='grey', linestyle=':')
F_adv_MOC_atl.mean('time').plot(label='Atlantic MOC', color='r', linestyle=':')
F_adv_MOC_rest.mean('time').plot(label='Indo Pacific MOC', color='b', linestyle=':')
plt.legend()
plt.grid()
plt.ylabel('PW');
In the above figure, we can see clearly how the overturning contributes to MHT. In general the overturning MHT curve has more extrema than the full MHT curve, suggesting that the non-overturning processes often oppose the overturnin in terms of heat transport.
In the Northern Hemisphere, the overturning MHT is very strong in the subtropics–likely associated with the shallow tropical overturning–and tends to zero by 45 N. The decomposition by basin shows in the Atlantic, overturning MHT and total Atlantic MHT are quite closely aligned at lower latitudes. The fact that the AMOC is positive in both hemispheres is clearly responsible for the overall northward MHT in the Atlantic; this can be seen from the form of the equation for overturning meridional heat transport:
Since both \(Psi\) and \(\partial \overline{\Theta} / \partial z\) are positive, the AMOC heat transport is positive.
The Southern Ocean is pretty interesting too. The overturning MHT is northwards, while the total advective MHT relatively close to zero. This suggests that some other advective process must be working in opposition to the overturning MHT. As we will see when we look deeper at the Southern Ocean, this other process is transport by the transient eddies and stationary meanders of the ACC.