$C_{00}$ | $1402.388$ | $C_{01}$ | $5.03830$ |
---|---|---|---|
$C_{02}$ | $-5.81090\times10^{-2}$ | $C_{03}$ | $3.3432\times10^{-4}$ |
$C_{04}$ | $-1.47797\times10^{-6}$ | $C_{05}$ | $3.1419\times10^{-9}$ |
$C_{10}$ | $0.153563$ | $C_{11}$ | $6.8999\times10^{-4}$ |
$C_{12}$ | $-8.1829\times10^{-6}$ | $C_{13}$ | $1.3632\times10^{-7}$ |
$C_{14}$ | $-6.1260\times10^{-10}$ | $C_{20}$ | $3.1260\times10^{-5}$ |
$C_{21}$ | $-1.7111\times10^{-6}$ | $C_{22}$ | $2.5986\times10^{-8}$ |
$C_{23}$ | $-2.5353\times10^{-10}$ | $C_{24}$ | $1.0415\times10^{-12}$ |
$C_{30}$ | $-9.7729\times10^{-9}$ | $C_{31}$ | $3.8513\times10^{-10}$ |
$C_{32}$ | $-2.3654\times10^{-12}$ | $A_{00}$ | $1.389$ |
$A_{01}$ | $-1.262\times10^{-2}$ | $A_{02}$ | $7.166\times10^{-5}$ |
$A_{03}$ | $2.008\times10^{-6}$ | $A_{04}$ | $-3.21\times10^{-8}$ |
$A_{10}$ | $9.4742\times10^{-5}$ | $A_{11}$ | $-1.2583\times10^{-5}$ |
$A_{12}$ | $-6.4928\times10^{-8}$ | $A_{13}$ | $1.0515\times10^{-8}$ |
$A_{14}$ | $-2.0142\times10^{-10}$ | $A_{20}$ | $-3.9064\times10^{-7}$ |
$A_{21}$ | $9.1061\times10^{-9}$ | $A_{22}$ | $-1.6009\times10^{-10}$ |
$A_{23}$ | $7.994\times10^{-12}$ | $A_{30}$ | $1.100\times10^{-10}$ |
$A_{31}$ | $6.651\times10^{-12}$ | $A_{32}$ | $-3.391\times10^{-13}$ |
$B_{00}$ | $-1.922\times10^{-2}$ | $B_{01}$ | $-4.42\times10^{-5}$ |
$B_{10}$ | $7.3637\times10^{-5}$ | $B_{11}$ | $1.7950\times10^{-7}$ |
$D_{00}$ | $1.727\times10^{-3}$ | $D_{10}$ | $-7.9836\times10^{-6}$ |
$C_{000}$ | $1402.392$ | $C_{T1}$ | $0.5012285\times10^1$ |
---|---|---|---|
$C_{T2}$ | $-0.551184\times10^{-1}$ | $C_{T3}$ | $0.221649\times10^{-3}$ |
$C_{S1}$ | $0.1329530\times10^{1}$ | $C_{S2}$ | $0.1288598\times10^{-3}$ |
$C_{P1}$ | $0.1560592$ | $C_{P2}$ | $0.2449993\times10^{-4}$ |
$C_{P3}$ | $-0.88333959\times10^{-8}$ | $C_{ST}$ | $-0.1275936\times10^{-1}$ |
$C_{TP}$ | $0.6353509\times10^{-2}$ | $C_{T2P2}$ | $0.265617\times10^{-7}$ |
$C_{TP2}$ | $-0.1593895\times10^{-5}$ | $C_{TP3}$ | $0.5222483\times10^{-9}$ |
$C_{T3P}$ | $-0.4383615\times10^{-6}$ | $C_{S2P2}$ | $-0.1616745\times10^{-8}$ |
$C_{ST2}$ | $0.9688441\times10^{-4}$ | $C_{S2TP}$ | $0.4857614\times10^{-5}$ |
$C_{STP}$ | $-0.3406824\times10^{-3}$ |
So, we see that the speed of sound changes linearly with depth when temperature and salinity is held constant (we will soon see that this is strictily due to the temperature being held constant). This makes sense after the analysis we made earlier with the MacKenzie equation, although similar analysis is much more difficult to see as the number of terms in a given equation increase -- note that all four equations agree with one another quite well.
What we also see is that if we lower the salinity to 21ppt instead of 30ppt, that Del Grosso's equation stops agreeing with the remaining three equations -- this salinity is indeed out of its claimed range of validity.
This Temperature vs. depth curve is called the Thermocline. I was unable to find an analytic function for this sort of curve from any online resources or textbooks. So my next attemp was to fit a 3rd order polynomial through 4 points that fall on this curve, but this was futile. Next I tried to fit a 9th order polynomial through 10 points, and this also had way too much variability between the points to be useful.
Finally what I ended up doing was to estimate the temperature every 100m by eyeballing the graph of the Thermocline as pictured above, and assuming that the temperature decreased linearly between each of these points. Clearly this could be improved upon by taking points more frequently, or by having an actual dataset to work with. The code below shows how I make the graphs, including the data points I used for the temperature.
import numpy as np
import matplotlib.pyplot as plt
# self-made function of thermocline
def thermocline(N):
T = []
therm_len = len(thermoline_points)
N_split = int(N/(therm_len-1))
i = 0
while i < therm_len-1:
temperature = np.linspace(thermoline_points[i][0], thermoline_points[i+1][0], N_split)
for t in temperature:
T.append(t)
i += 1
return T
# function for MacKenzie equation
def mac(t, s, z):
speed = 1448.96 + 4.59*t - 0.05304*t**2 + 0.0002374*t**3 + 0.0160*z \
+ (1.340-0.01025*t)*(s-35) + (1.675e-7)*z**2 - (7.139e-13)*t*z**3
return speed
# function for Coppens equation
def cop(t, s, z):
t /= 10 # temp divided by 10
z *= 0.001 # depth in km
c0 = 1449.05 + 45.7*t - 5.21*t**2 + 0.23*t**3 + (1.333-0.126*t+0.009*t**2)*(s-35)
speed = c0 + (16.23+0.253*t)*z + (0.213-0.1*t)*z**2 + (0.016+0.0002*(s-35))*(s-35)*t*z
return speed
# function to find density of water as function of temp
def rho_h2o(t):
beta = 0.0002
return 1000/(1+beta*(t-20))
# function for UNESCO (Chen and Millero) equation (P in bar)
def unesco(t, s, z):
# use pascal's law for primitive way to convert depth to pressure
rho = rho_h2o(t)
g = 9.80665
p0 = 101.325*0.01 # atm pressure from kPa to bar
p = p0 + (rho*g*z)*1e-5 # convert rho*g*z from Pa to bar
# define all constants
c00,c01,c02,c03,c04,c05 = 1402.388,5.03830,-5.81090e-2,3.3432e-4,-1.47797e-6,3.1419e-9
c10,c11,c12,c13,c14 = 0.153563,6.8999e-4,-8.1829e-6,1.3632e-7,-6.1260e-10
c20,c21,c22,c23,c24 = 3.1260e-5,-1.7111e-6,2.5986e-8,-2.5353e-10,1.0415e-12
c30,c31,c32 = -9.7729e-9,3.8513e-10,-2.3654e-12
a00,a01,a02,a03,a04 = 1.389,-1.262e-2,7.166e-5,2.008e-6,-3.21e-8
a10,a11,a12,a13,a14 = 9.4742e-5,-1.2583e-5,-6.4928e-8,1.0515e-8,-2.0142e-10
a20,a21,a22,a23 = -3.9064e-7,9.1061e-9,-1.6009e-10,7.994e-12
a30,a31,a32 = 1.100e-10,6.651e-12,-3.391e-13
b00,b01 = -1.922e-2,-4.42e-5
b10,b11 = 7.3637e-5,1.7950e-7
d00,d10 = 1.727e-3,-7.9836e-6
# calculate speed
d = d00 + d10*p
b = b00 + b01*t + (b10 + b11*t)*p
a = (a00 + a01*t + a02*t**2 + a03*t**3 + a04*t**4) +\
(a10 + a11*t + a12*t**2 + a13*t**3 + a14*t**4)*p +\
(a20 + a21*t + a22*t**2 + a23*t**3)*p**2 +\
(a30 + a31*t + a32*t**2)*p**3
c_w = (c00 + c01*t + c02*t**2 + c03*t**3 + c04*t**4 + c05*t**5) +\
(c10 + c11*t + c12*t**2 + c13*t**3 + c14*t**4)*p +\
(c20 + c21*t + c22*t**2 + c23*t**3 + c24*t**4)*p**2 +\
(c30 + c31*t + c32*t**2)*p**3
speed = c_w + a*s + b*s**(3/2) + d*s**2
return speed
# create a function for the Del Grosso Equation (Wong and Zhu) (P in kg/cm^2)
def del_grosso(t, s, z):
# use pascal's law for primitive way to convert depth to pressure
rho = rho_h2o(t)
g = 9.80665
p0 = 1.033 # atm pressure from kPa to kg/cm^3
p = p0 + (rho*g*z)*1.033/101325 # convert rho*g*z from Pa to kg/cm^3
# define constants
c000,cT1,cT2,cT3 = 1402.392,0.5012285e1,-0.551184e-1,0.221649e-3
cS1,cS2,cP1,cP2,cP3 = 0.1329530e1,0.1288598e-3,0.1560592,0.2449993e-4,-0.8833959e-8
cST,cTP,cT2P2,cTP2 = -0.1275936e-1,0.6353509e-2,0.2656174e-7,-0.1593895e-5
cTP3,cT3P,cS2P2,cST2 = 0.5222483e-9,-0.4383615e-6,-0.1616745e-8,0.9688441e-4
cS2TP,cSTP = 0.4857614e-5,-0.3406824e-3
# calculate speed
dcSTP = cTP*t*p + cT3P*t**3*p + cTP2*t*p**2 + cT2P2*(t*p)**2 + cTP3*t*p**3 +\
cST*s*t + cST2*s*t**2 + cSTP*s*t*p + cS2TP*s**2*t*p + cS2P2*(s*p)**2
dcP = cP1*p + cP2*p**2 + cP3*p**3
dcS = cS1*s + cS2*s**2
dcT = cT1*t + cT2*t**2 + cT3*t**3
speed = c000 + dcT + dcS + dcP + dcSTP
return speed
# set some constant value for salinity, and note how many steps we want
s = 21
N = 6000
# Create an array of points that fall on the thermoline (estimate) to generate linear
# temperatures between (every 100m of depth)
thermoline_points = [(24,0),(23.8,100),(23.5,200),(23,300),(22,400),(21,500),\
(17.5,600),(13,700),(9.8,800),(8.2,900),(7,1000),\
(6.5,1100),(6,1200),(5.5,1300),(5.25,1400),(5,1500),\
(4.9,1600),(4.8,1700),(4.7,1800),(4.6,1900),(4.5,2000),\
(4.47,2100),(4.44,2200),(4.41,2300),(4.38,2400),(4.35,2500),\
(4.32,2600),(4.29,2700),(4.26,2800),(4.23,2900),(4.2,3000),\
(4.17,3100),(4.14,3200),(4.10,3300),(4.06,3400),(4.0,3500),\
(3.93,3600),(3.85,3700),(3.78,3800),(3.7,3900),(3.63,4000),\
(3.55,4100),(3.48,4200),(3.4,4300),(3.33,4400),(3.25,4500),\
(3.17,4600),(3.09,4700),(3.0,4800),(2.92,4900),(2.84,5000),\
(2.76,5100),(2.68,5200),(2.6,5300),(2.52,5400),(2.44,5500),\
(2.36,5600),(2.28,5700),(2.2,5800),(2.1,5900),(2.0,6000)]
# range of depth
depth = np.linspace(0,6000,N)
# temps at some depth
temps = thermocline(N)
# get speeds as function of depth and temp
mac_constS_speed = []
cop_constS_speed = []
unesco_constS_speed = []
del_grosso_constS_speed = []
i = 0
while i len(depth):
mac_constS_speed.append(mac(temps[i], s, depth[i]))
cop_constS_speed.append(cop(temps[i], s, depth[i]))
unesco_constS_speed.append(unesco(temps[i], s, depth[i]))
del_grosso_constS_speed.append(del_grosso(temps[i], s, depth[i]))
i += 1
with plt.rc_context({'axes.edgecolor':'white','xtick.color':'white','ytick.color':'white',\
'figure.facecolor':'darkslategrey','axes.facecolor':'darkslategrey',\
'axes.labelcolor':'white','axes.titlecolor':'white'}):
fig = plt.figure(figsize=(8,6))
ax = plt.subplot(111)
ax.plot(depth, mac_constS_speed, 'c', label="MacKenzie")
ax.plot(depth, cop_constS_speed, 'm', label="Coppens")
ax.plot(depth, unesco_constS_speed, color="orange", label="UNESCO")
ax.plot(depth, del_grosso_constS_speed, color="lime", label="Del Grosso")
ax.legend()
ax.set_title("Speed of Sound in Ocean (constant T)")
plt.savefig("../images/speed_var_T.png")
Again, we see that all of the equations agree with one another quite well, with the Coppens equation varying once it goes beyond a depth of 4000m.
We see that ther is a minumum at around 1200m. This is actually a fascinating result, and we call this region of minimum speed the sound channel. Sound traveling along this channel is able to travel very far distances (sometimes halfway around the entire globe). This happens due to refraction -- due to the increase in speed, sound rays pointing upward at a small enough angle are able to bend back downward toward the channel, and rays pointing downward at a small enough angle are able to bend back upward toward the channel.
This is a good page to gain a little more intuition about the refraction of sound waves.
The depth of the sound channel obviously depends on water conditions, but is most generally determined by geographic regions; tending to be near the surface (as close as 10m below sea level) at high latitudes.
# importing mplot3d toolkits
from mpl_toolkits import mplot3d
from pylab import *
# defining axes
salinity = np.linspace(0,40,N)
# get speeds as function of depth
mac_varS_speed = []
cop_varS_speed = []
unesco_varS_speed = []
del_grosso_varS_speed = []
salinity_3D = []
depth_3D = []
temp_3D = []
for s in salinity:
i = 0
while i len(depth):
salinity_3D.append(s)
depth_3D.append(depth[i])
temp_3D.append(temps[i])
mac_varS_speed.append(mac(temps[i], s, depth[i]))
cop_varS_speed.append(cop(temps[i], s, depth[i]))
unesco_varS_speed.append(unesco(temps[i], s, depth[i]))
del_grosso_varS_speed.append(del_grosso(temps[i], s, depth[i]))
i += 1
with plt.rc_context({'axes.edgecolor':'white','xtick.color':'white','ytick.color':'white',\
'figure.facecolor':'darkslategrey','axes.facecolor':'darkslategrey',\
'axes.labelcolor':'white','axes.titlecolor':'white'}):
fig = plt.figure(figsize=(12,12))
# syntax for 3-D projection
ax = plt.axes(projection ='3d')
scat_plot = ax.scatter(salinity_3D, del_grosso_varS_speed, depth_3D, c=temp_3D, cmap="cool")
color_map = plt.get_cmap('cool')
cbar = plt.colorbar(scat_plot)
cbar.set_label("Temperature (C)", fontsize=20)
ax.set_title('Del Grosso Equation', fontsize=24)
ax.set_xlabel("Salinity (ppt)", fontsize=20)
ax.set_ylabel("Speed (m/s)", fontsize=20)
ax.set_zlabel("Depth (m)", fontsize=20)
# syntax for plotting
ax.view_init(185, 205)
plt.savefig('../images/delGrosso_image.png')
plt.show()
The main take away from these plots is that the speed of sound in water tends to increase with salinity. In this case, the MacKenzie equation and the UNESCO equation appear to increase linearly with salinity, and look almost identical. The Coppens equation and Del Grosso's equation appear to increase with salinity in a nonlinear fashion, and also look very similar to one another.
Finally, I made an animation of the MacKenzie equation plot rotating about 360 degrees.
from matplotlib.animation import FuncAnimation
with plt.rc_context({'axes.edgecolor':'white','xtick.color':'white','ytick.color':'white',\
'figure.facecolor':'darkslategrey','axes.facecolor':'darkslategrey',\
'axes.labelcolor':'white','axes.titlecolor':'white'}):
fig = plt.figure(figsize=(12,12))
ax = plt.axes(projection ='3d')
color_map = cm.ScalarMappable(cmap="cool")
color_map.set_array(temp_3D)
cbar = plt.colorbar(color_map)
cbar.set_label("Temperature (C)", fontsize=20)
ax.set_title('MacKenzie Equation', fontsize=24)
ax.set_xlabel("Salinity (ppt)", fontsize=20)
ax.set_ylabel("Speed (m/s)", fontsize=20)
ax.set_zlabel("Depth (m)", fontsize=20)
ax.scatter(salinity_3D, mac_varS_speed, depth_3D, c=temp_3D, cmap="cool")
def update(i, fig, ax):
ax.view_init(elev=-185, azim=i)
return fig, ax
anim = FuncAnimation(fig, update, frames=np.arange(0, 360, 0.3), repeat=True, fargs=(fig, ax))
anim.save('../images/mackenzie.mp4', dpi=80, writer='ffmpeg', fps=40)