The hydrostatic balance

You have already done this first part as an exercise week 5.

The pressure (\(P\)) in the athmosphere and in the interior ocean can be described in a first approximation as hydrostatic. The pressure (in the unit Pascal), that a particle located at the depth \(z\) in the ocean (where the surface has depth \(0\)) is subjected to is described by the following equation:

\[P(z) = -\rho \cdot g \cdot z\]

where \(\rho\) is the water density (with the unit \(\text{kg} \cdot \text{m}^{-3}\)), \(g\) is the acceleration of gravity (with the unit \(\text{m} \cdot \text{s}^{-2}\)) and \(z\) is the depth of the particle (with the unit \(\text{m}\)). The SI-unit for pressure is Pascal \([\text{Pa}]\), but in Meteorology and Oceangraphy pressure is usually presented in the unit decibar \([\text{dbar}]\). These to correspond according to the formula: \(1\,\text{Pa} = 10^{-4}\,\text{dbar}\).

We write function called hyd_pres() that calculates the pressure at a given depth in the unit \(\text{dbar}\). The arguments of the function are: a density value (\(\rho\)), the acceleration of gravity (\(g\)) and the depth (\(z\)).

def hyd_pres(dens, g, z):
    P = -dens * g * z  # calculate pressure in Pa
    P = P / 1e4  # convert to dbar
    return P

#define constants
g = 10 # [m s-2]
dens = 1025 # [kg m-3]

#calculate the hydrostatic pressure at 100 m
hydP100 = hyd_pres(dens,g,-100)

print("The pressure is %f dbar"%(hydP100))

This gives us the following output:

The pressure is 102.500000 dbar

We will now investigate a bit more about this topic. First things first, let’s understand the physics behind it.

Fluid mechanics is the core of dynamical Oceanography and Meteorology. As such, this field is concerned to describe and reproduce the motion of fluids, such as water and air. The set of equations that describe how forces act on the fluid to put it in motion is known as the Navier-Stokes equations and it is directly derived from Newton’s second law: \(F = m\, a\), where \(F\) is the force applied in the studied object (\(N\)), \(m\) is the object mass (\(kg\)) and \(a\) is its acceleration (\(m\, s^{-2}\)).

We can decompose the various forces into the 3D reference frame with coordinates \(x\), \(y\) and \(z\). \(x\) and \(y\) represent the horizontal plane such as the land or ocean surface and \(z\) represents the vertical axis upward into the atmosphere or downward into the ocean interior. Here, we will focus on the vertical axis, and explore a simple and useful force balance called hydrostatic balance.

The hydrostatic balance is a strong assumption in geophysical fluid flows and it describes the balance between the gravity force (pointing downwards) and the gradient pressure force (pointing upwards). In the ocean, one can imagine it as the weight of the water column felt by a particle located at a certain depth. Such a balance is expressed by:

\[g = -\frac{1}{\rho}\frac{dP}{dz},\]

where \(g\) is the acceleration of gravity (\(m\, s^{-2}\)), \(\rho\) is the water density (\(kg\, m^{-3}\)) and \(\frac{dP}{dz}\) is the pressure gradient (\(Pa\, m^{-1}\)). To find an expression for pressure at a given depthin the ocean (we call this depth \(z_1\)), we can integrate the equation with respect to \(z\) from \(z_1\) to the ocean free surface (\(z_{surf}\)):

\[\int_{z_1}^{z_{surf}} \frac{dP}{dz} \,dz = -\rho g \int_{z_1}^{z_{surf}} \,dz = -\rho g [z_{surf} - z_1] = -\rho g \,\delta z\]

Air is much lighter than water, and we may therefore assume that the pressure at the ocean surface is negligible compared to the pressure in the ocean interior. This assumptions yields \(P(z) \sim 0\), for \(z \rightarrow\) \(z_{surf}\). We can also express the difference in depth between the surface and \(-z_1\) as \(\delta z\). If we regard \(z_{surf}\) to be zero, \(\delta z\) will be the same as the depth you are seeking the pressure for.

We are now left with a simple formula for hydrostatic pressure at the given depth \(z\):

\[P(z) = -\rho g\, z\]

This is the same equation that you manipulated in the previous assingment.

Task 1:

Given the hyd_pres function that we created before, calculate the pressure from \(z = 0\) to \(z = -1000\) in increments of \(10\; m\). How much does the pressure increase for every \(10\; m\) depth? Consider \(\rho = 1025\; kg\, m^{-3}\) and \(g = 10\; m\, s^{-2}\). (Hint: Start by making a depth array with the Numpy linspace function.)

## Task 1

# import necessary toolboxes
import numpy as np

#define constants
g = 10 # [m s-2]
dens = 1025 # [kg m-3]

# create array with depths every 10m
z_depths = np.linspace(0, -1000, 101)
# alternatively you can use np.arange
# z_depths=np.arange(-1000.,0.,10)

#calculate the hydrostatic pressure at each depth, assuming constant density
hydP1 = hyd_pres(dens, g, z_depths)

mean_diff = np.mean(np.diff(hydP1))

print("For every 10m, the pressure increases by %f dbar"%(mean_diff))

We get the following result:

For every 10m, the pressure increases by 10.250000 dbar

In other words, you increase the pressure by aroud \(10\; dbar\) for every \(10\; m\) deeper in the ocean. If you transfer this result to a more concrete example, the pressure we feel at the ocean surface is \(1\; atm = 10\; dbar\). You would experience \(100\) times more pressure at \(100\; m\) depth in the ocean in comparison to when you are at the surface!

Task 2:

a) Calculate the pressure at \(100\; m\) depth for density values ranging from \(0\) to \(1025\; kg\, m^{-3}\). (Tip: use the Numpy ’arange’ function.)

b) What is the mean rate of pressure increase for every \(\delta \rho = 5\; kg\, m^{-3}\)?

c) Is the pressure increase constant?

## Task 2
dens2 = np.arange(0, 1025 + 5, 5)

# a)
hydP2 = hyd_pres(dens2, g, -100)
# print(hydP2)

# b)
pres_incr = np.diff(hydP2)
mean_inc = np.mean(pres_incr)
print("The mean rate of pressure increase is %f dbar" % (mean_inc))

# c)
all_same = np.all(pres_incr == pres_incr[0])
if all_same:
    print("The pressure increase is constant.")
else:
    print("The pressure increase is not constant.")

We get the following output.

The mean rate of pressure increase is 0.500000 dbar
The pressure increase is constant.

Task 3:

The file zpd_file.txt contains pressure, temperature and salinity values obtained from the Levitus 94 dataset in the coordinates 12.5\(^{\circ}\)S and 30.5\(^{\circ}\)W. The Levitus dataset gathers data from different sources (research institutes), field campaings and variables (temperature, salinity, oxygen and nutrients). All of this data receives a quality control check and processing procedures before they are made available to the community.

We will compare the pressure and density values obtained by using the hydrostatic balance function hyd_pres that we created before with the pressure and density data contained in the zpd_file.txt.

a) First of all, we open the file using the Pandas function read_csv with column separator sep = r'\s+’. We define the variables df_z, df_p and df_d. These variables will store the depths, pressure and density values present in the zpd_file.txt.

## Task 3
import pandas as pd

#read the data into a pandas dataframe
df = pd.read_csv('zpd_file.txt', sep=r'\s+')

df_z = df.depth
df_p = df.press
df_d = df.dens

b) By using the hyd_pres function, we calculate the pressure for the depths present in the file (df_z). Since density is a required parameter in the function, use the first value of df_d as the argument.

#calculate the hydrostatic pressure from the Levitus dataset. df_d[0] is the surface density
p_hyd = hyd_pres(df_d[0], g, df_z) # use the function

print(p_hyd)

We get the following output:

0       -0.000000
1       10.238348
2       20.476697
3       30.715045
4       51.191742
5       76.787612
6      102.383483
7      127.979354
8      153.575225
9      204.766966
10     255.958708
11     307.150449
12     409.533932
13     511.917415
14     614.300898
15     716.684381
16     819.067864
17     921.451347
18    1023.834830
Name: depth, dtype: float64

c) We compare this result with the observed pressure df_p. How much do they differ?

#calculate the difference from the pressure given in the dataset
dif_p = p_hyd - df_p # make the difference

print(dif_p)

We get the following result:

0     -0.000000
1      0.176081
2      0.351712
3      0.526892
4      0.875902
5      1.309629
6      1.740539
7      2.168632
8      2.593905
9      3.435990
10     4.266789
11     5.086293
12     6.691385
13     8.251207
14     9.765696
15    11.234793
16    12.658436
17    14.036563
18    15.369112
dtype: float64

Ok, so we have essentially a difference of 0-15% between our values estimated with the hydrostatic balance assumption and the observed ones. Why do you think the differences are not the same throughout the water column?

Task 4:

a) We have been assuming so far that the density is constant throughout the water column (or atmosphere), but in fact it also varies with depth, i.e., \(\rho\) is a function of \(z\). Now, instead of finding pressure, let’s do it the other way round: use pressure values, depth levels and the acceleration of gravity to estimate densities.

\[\frac{d \rho}{dz} = \frac{-1}{g \delta z} \frac{dP}{dz} \label{eq:dpdz} \tag{1}\]

For this, we need to use an interactive method.

Step 1: Create the lists d_hyd and d_o containing the first observed density value from df_d as an initial item. For each interaction in the loop, the new values of density will be stored in these lists.

d_hyd = [df_d[0]] # to store density values calculated from p_hydrostatic
d_o = [df_d[0]] # to store density values obtained from the dataset

Step 2: Create the empty lists dp_dz_h (pressure from hydrostatic balance) and dp_dz_o (pressure from file). These are the lists where the \(\frac{dP}{dz}\) and \(dz\) values will be saved.

dp_dz_h = [] # rate of change of p_hydrostatic w.r.t z
dp_dz_o = [] # rate of change of p_observed w.r.t z

Step 3: Create the function diff_dp_dz, which takes as arguments the pressure, the water depth and the list that the rates will be stored. This function will calculate the rate of change of pressure (\(\frac{dP}{dz}\)) with respect to \(z\). For example, using the first two values provided in the zpd_file.txt:

\[\frac{P(i+1) - P(i)}{z(i+1) - z(i)} \rightarrow \frac{10.06 - 0}{-10-0} = -1.006 \hspace{0.1cm} \text{dbar m$^{-1}$}\]

where \(i\) is every step in your list (in our case, variables in Task 3 have length 19 so \(i\) ranges from \(0\) to \(18\)). Use the \(z\) depths provided in the zpd_file.txt.

Tips: Use a for loop within the diff_dp_dz ranging from \(0\) to \(17\). We can’t loop til the last point (\(i = 18\)) because there is no further values beyond it (\(i+1\), in the previous example). Use the function append to store the values in the dp_dz_(h,o) lists. If you struggle with the differentiation, try the function np.diff from Numpy.

def diff_dp_dz(p, z, lst):
    for i in range(0, len(df_p)-1):
        dp = p[i+1] - p[i] # diff p
        dz = z[i+1] - z[i] # diff z
        dpdz = dp/dz # take the rate

        lst.append(dpdz) # store value
    return

diff_dp_dz(p_hyd, df_z, dp_dz_h)
diff_dp_dz(df_p, df_z, dp_dz_o)

We have halfway done. To summarize, we calculated \(\frac{dP}{dz}\) from Equation 1 for pressure values obtained with the hyd_pres function and for the observed ones provided in the file.

Now, create the function dens_from_pres taking as arguments \(\frac{dP}{dz}\), \(g\) and the list where the calculated density values will be stored (d_hyd and d_o). Based on the hydrostatic balance, this function must provide the calculated density values given \(P\), \(z\) and \(g\). To clarify how the interactive method works, let’s expand Eq. 1 as:

\[\frac{\rho(i+1) - \rho(i)}{dz} = \frac{-1}{g \delta z}\frac{dP}{dz}\]
\[\Leftrightarrow \rho(i+1) = \rho(i) - dz\frac{1}{g \delta z}\frac{P(i+1) - P(i)}{dz}\]
\[\Leftrightarrow \rho(i+1) = \rho(i) - \frac{1}{g}\frac{P(i+1) - P(i)}{z(i+1) - z(i)} \tag{2}\]

So, the new value of density (\(\rho (i+1)\)) is given by the density at the water depth above (\(\rho(i)\)) minus the rate of change of pressure between the two levels (\(\frac{dP}{dz}\)) and divided by the acceleration of gravity (\(g\)).

To make it work, use again a for-loop within the dens_from_pres function ranging from \(0\) to \(17\). For each interaction (i+1), you must access the density (d_hyd, d_o) and dp_dz (dp_dz_h, dp_dz_o) values stored in the previous time steps (i), make the correct arithmetic provided by Eq. 2 and store the new density value in the same density list.

d_hyd = [df_d[0]] # density values calculated from p_hydrostatic
d_o = [df_d[0]] # density values obtained from the dataset


def dens_from_pres(dpdz, g, den):
    for i in range(0, len(dpdz)):
        dens = den[i] - (1/g)*(dpdz[i])
        den.append(dens)
    return

dens_from_pres(dp_dz_o, g, d_o)
dens_from_pres(dp_dz_h, g, d_hyd)

b) Ok, so now we have density values calculated from pressure obtained by using the hyd_pres function and from the data file!

As a next step, we calculate the difference between d_o and d_hyd.

# difference between estimated values
diff_dens = np.array(d_hyd) - np.array(d_o)

print(diff_dens)

We get the following output:

[0.         0.00176081 0.00351712 0.00526892 0.00701397 0.00874888
 0.01047252 0.01218489 0.01388598 0.01557015 0.01723175 0.01887076
 0.02047585 0.02203567 0.02355016 0.02501926 0.0264429  0.02782103
 0.02915358]

The difference between them is actually really small (around 0.1% to 3%).

c) As the last example, we calculate the difference between the observed density values (df_d) and (d_o, d_hyd). Plot the estimated (d_o) and observed (df_d) densities with Matplotlib.

import matplotlib.pyplot as plt

# difference between observed density and the estimated ones
diff_o_hyd = df_d - np.array(d_hyd)
diff_o_o = df_d - np.array(d_o)

plt.plot(df_d, df_z, 'r--', label='Observed')
plt.plot(d_o, df_z, 'k-', label='Estimated')

plt.xlabel('Density [kg m-3]')
plt.ylabel('Depth [m]')

plt.legend(loc='upper right')
plt.show()

We get the following plot:

../../_images/plot1.png

Why are they so different?