You are on page 1of 6

23/07/2020 Exploring (de)compaction with Python « Hindered Settling

 April  12 Exploring (de)compaction with Python


2017

All clastic sediments are subject to compaction (and reduction of porosity) as the result of increasingly
5 COMMENTS tighter packing of grains under a thickening overburden. Decompaction – the estimation of the
decompacted thickness of a rock column – is an important part of subsidence (or geohistory) analysis.
The following exercise is loosely based on the excellent basin analysis textbook by Allen & Allen (2013),
especially their Appendix 56. You can download the Jupyter notebook version of this post from Github.

Import stuff

1 import numpy as np
2 import matplotlib.pyplot as plt
3 import functools
4 from scipy.optimize import bisect
5 %matplotlib inline
6 %config InlineBackend.figure_format = 'svg'
7 plt.rcParams['mathtext.fontset'] = 'cm'

Posing the problem

Given a sediment column of a certain lithology with its top at   and its base at  , we are trying to find
the thickness and average porosity of the same sediment column at a different depth (see figure below).
We are going to set the new top   and work towards finding the new base  .

1 plt.figure(figsize=(2,5))
2 x = [0,1,1,0,0]
3 y = [1,1,1.5,1.5,1]
4 plt.text(-0.6,1.02,'$y_1$',fontsize=16)
5 plt.text(-0.6,1.52,'$y_2$',fontsize=16)
6 plt.text(-0.6,1.27,'$\phi$',fontsize=16)
7 plt.fill(x,y,'y')
8 x = [3,4,4,3,3]
9 y = [0.5,0.5,1.15,1.15,0.5]
10 plt.text(2.25,0.52,'$y_1\'$',fontsize=16)
11 plt.text(2.25,1.17,'$y_2\'$',fontsize=16)
12 plt.text(2.25,0.9,'$\phi\'$',fontsize=16)
13 plt.fill(x,y,'y')
14 plt.plot([1,3],[1,0.5],'k--')
15 plt.plot([1,3],[1.5,1.15],'k--')
16 plt.gca().invert_yaxis()
17 plt.axis('off');

https://hinderedsettling.com/2017/04/12/exploring-decompaction-with-python/ 1/6
23/07/2020 Exploring (de)compaction with Python « Hindered Settling

Porosity decrease with depth

Porosity decreases with depth, initially largely due to mechanical compaction of the sediment. The
decrease in porosity is relatively large close to the seafloor, where sediment is loosely packed; the lower
the porosity, the less room there is for further compaction. This decrease in porosity with depth is
commonly modeled as a negative exponential function (Athy, 1930):

where   is the porosity at depth   and   is the depth where the initial porosity   was reduced by  .

This is an empirical equation, as there is no direct physical link between depth and porosity; compaction
and porosity reduction are more directly related to the increase in effective stress under a thicker
overburden. Here we only address the simplest scenario with no overpressured zones. For normally
pressured sediments, Athy’s porosity-depth relationship can be expressed in a slightly different form:

where c is a coefficient with the units  . The idea is that c is a characteristic constant for a certain
lithology and it can measured if porosity values are available from different depths. Muds have higher
porosities at the seafloor than sands but they compact faster than sands. The plot below show some
typical curves that illustrate the exponential decrease in porosity with depth for sand and mud. The
continuous lines correspond to the parameters for sand and mud in Appendix 56 of Allen & Allen
(2013); the dashed lines are exponential fits to data from the Ocean Drilling Program (Kominz et al.,
2011).

https://hinderedsettling.com/2017/04/12/exploring-decompaction-with-python/ 2/6
23/07/2020 Exploring (de)compaction with Python « Hindered Settling
1 c_sand = 0.27 # porosity-depth coefficient for sand (km-1)
2 c_mud = 0.57 # porosity-depth coefficent for mud (km-1)
3 phi_sand_0 = 0.49 # surface porosity for sand
4 phi_mud_0 = 0.63 # surface porosity for mud
5 y = np.arange(0,3.01,0.01)
6  
7 phi_sand = phi_sand_0 * np.exp(-c_sand*y)
8 phi_mud = phi_mud_0 * np.exp(-c_mud*y)
9  
10 plt.figure(figsize=(4,7))
11 plt.plot(phi_sand,y,'y',linewidth=2,label='sand')
12 plt.plot(phi_mud,y,'brown',linewidth=2,label='mud')
13 plt.xlabel('porosity')
14 plt.ylabel('depth (km)')
15 plt.xlim(0,0.65)
16 plt.gca().invert_yaxis()
17  
18 c_sand = 1000/18605.0 # Kominz et al. 2011 >90% sand curve
19 c_mud = 1000/1671.0 # Kominz et al. 2011 >90% mud curve
20 phi_sand_0 = 0.407 # Kominz et al. 2011 >90% sand curve
21 phi_mud_0 = 0.614 # Kominz et al. 2011 >90% mud curve
22 phi_sand = phi_sand_0 * np.exp(-c_sand*y)
23 phi_mud = phi_mud_0 * np.exp(-c_mud*y)
24 plt.plot(phi_sand,y,'y--',linewidth=2,label='90% sand')
25 plt.plot(phi_mud,y,'--',color='brown',linewidth=2,label='90% mud')
26 plt.legend(loc=0, fontsize=10);

While the compaction trends for mud happen to be fairly similar in the plot above, the ones for sandy
lithologies are very different. This highlights that porosity-depth curves vary significantly from one
basin to another, and are strongly affected by overpressures and exhumation. Using local data and
geological information is critical. As Giles et al. (1998) have put it, “The use of default compaction curves
can introduce significant errors into thermal history and pore- fluid pressure calculations, particularly
where little well data are available to calibrate the model.” To see how widely – and wildly – variable
compaction trends can be, check out the review paper by Giles et al. (1998).

https://hinderedsettling.com/2017/04/12/exploring-decompaction-with-python/ 3/6
23/07/2020 Exploring (de)compaction with Python « Hindered Settling

Deriving the general decompaction equation

Compacting or decompacting a column of sediment means that we have to move it along the curves in
the figure above. Let’s consider the volume of water in a small segment of the sediment column (over
which porosity does not vary a lot):

As we have seen before, porosity at depth y is

The first equation then becomes

But

and

where   and   are the thicknesses that the water and total volumes occupy respectively, and A is the
area of the column we are looking at. So the relationship is equivalent to

If we integrate this over the interval   to   we get

Integrating this yields

As the total thickness equals the sediment thickness plus the water “thickness”, we get

The decompacted value of   is

Now we can write the general decompaction equation:

https://hinderedsettling.com/2017/04/12/exploring-decompaction-with-python/ 4/6
23/07/2020 Exploring (de)compaction with Python « Hindered Settling
That is,

The average porosity at the new depth will be

Write code to compute (de)compacted thickness

The decompaction equation could be solved in the ‘brute force’ way, that is, by gradually changing the
value of   until the right hand side (RHS) of the equation is the same as the left hand side (LHS) – see
for example the Excel spreadsheet that accompanies Appendix 56 in Allen & Allen (2013). However, we
(and scipy) can do better than that; we will use bisection, one the simplest optimization methods to find
the root of the function that we set up as RHS-LHS.

1 # compaction function - the unknown variable is y2a


2 def comp_func(y2a,y1,y2,y1a,phi,c):
3     # left hand side of decompaction equation:
4     LHS = y2a - y1a
5     # right hand side of decompaction equation:
6     RHS = y2 - y1 - (phi/c)*(np.exp(-c*y1)-np.exp(-c*y2)) + (phi/c)*(np.e
7     return LHS - RHS

Now we can do the calculations; here we set the initial depths of a sandstone column   to 2 and 3
kilometers, and we estimate the new thickness and porosity assuming that the column is brought to the
surface ( ).

1 c_sand = 0.27 # porosity-depth coefficient for sand (km-1)


2 phi_sand = 0.49 # surface porosity for sand
3 y1 = 2.0 # top depth in km
4 y2 = 3.0 # base depth in km
5 y1a = 0.0 # new top depth in km

One issue we need to address is that ‘comp_func’ six input parameters, but the scipy ‘bisect’ function
only takes one parameter. We create a partial function ‘comp_func_1’ in which the only variable is ‘y2a’,
the rest are treated as constants:

1 comp_func_1 = functools.partial(comp_func, y1=y1, y2=y2, y1a=y1a, phi=ph


2 y2a = bisect(comp_func_1,y1a,y1a+3*(y2-y1)) # use bisection to find new
3 phi = (phi_sand/c_sand)*(np.exp(-c_sand*y1)-np.exp(-c_sand*y2))/(y2-y1)
4 phia = (phi_sand/c_sand)*(np.exp(-c_sand*y1a)-np.exp(-c_sand*y2a))/(y2a-
5  
6 print('new base depth: '+str(round(y2a,2))+' km')
7 print('initial thickness: '+str(round(y2-y1,2))+' km')
8 print('new thickness: '+str(round(y2a-y1a,2))+' km')
9 print('initial porosity: '+str(round(phi,3)))
10 print('new porosity: '+str(round(phia,3)))

Write code to (de)compact a stratigraphic column with


multiple layers

Next we write a function that does the depth calculation for more than one layer in a sedimentary
column:

https://hinderedsettling.com/2017/04/12/exploring-decompaction-with-python/ 5/6
23/07/2020 Exploring (de)compaction with Python « Hindered Settling
1 def decompact(tops,lith,new_top,phi_sand,phi_mud,c_sand,c_mud):
2     tops_new = [] # list for decompacted tops
3     tops_new.append(new_top) # starting value
4     for i in range(len(tops)-1):
5         if lith[i] == 0:
6             phi = phi_mud; c = c_mud
7         if lith[i] == 1:
8             phi = phi_sand; c = c_sand
9         comp_func_1 = functools.partial(comp_func,y1=tops[i],y2=tops[i+1
10         base_new_a = tops_new[-1]+tops[i+1]-tops[i]
11         base_new = bisect(comp_func_1, base_new_a, 4*base_new_a) # bisec
12         tops_new.append(base_new)
13     return tops_new

Let’s use this function to decompact a simple stratigraphic column that consists of 5 alternating layers of
sand and mud.

1 tops = np.array([1.0,1.1,1.15,1.3,1.5,2.0])
2 lith = np.array([0,1,0,1,0]) # lithology labels: 0 = mud, 1 = sand
3 phi_sand_0 = 0.49 # surface porosity for sand
4 phi_mud_0 = 0.63 # surface porosity for mud
5 c_sand = 0.27 # porosity-depth coefficient for sand (km-1)
6 c_mud = 0.57 # porosity-depth coefficent for mud (km-1)
7 tops_new = decompact(tops,lith,0.0,phi_sand_0,phi_mud_0,c_sand,c_mud) # c

Plot the results:

1 def plot_decompaction(tops,tops_new):
2     for i in range(len(tops)-1):
3         x = [0,1,1,0]
4         y = [tops[i], tops[i], tops[i+1], tops[i+1]]
5         if lith[i] == 0:
6             color = 'xkcd:umber'
7         if lith[i] == 1:
8             color = 'xkcd:yellowish'
9         plt.fill(x,y,color=color)
10         x = np.array([2,3,3,2])
11         y = np.array([tops_new[i], tops_new[i], tops_new[i+1], tops_new[
12         if lith[i] == 0:
13             color = 'xkcd:umber'
14         if lith[i] == 1:
15             color = 'xkcd:yellowish'
16         plt.fill(x,y,color=color)
17     plt.gca().invert_yaxis()
18     plt.tick_params(axis='x',which='both',bottom='off',top='off',labelbo
19     plt.ylabel('depth (km)');
20  
21 plot_decompaction(tops,tops_new)

Now let’s see what happens if we use the 90% mud and 90% sand curves from Kominz et al. (2011).

1 tops = np.array([1.0,1.1,1.15,1.3,1.5,2.0])
2 lith = np.array([0,1,0,1,0]) # lithology labels: 0 = mud, 1 = sand
3 c_sand = 1000/18605.0 # Kominz et al. 2011 >90% sand curve
4 c_mud = 1000/1671.0 # Kominz et al. 2011 >90% mud curve
5 phi_sand_0 = 0.407 # Kominz et al. 2011 >90% sand curve
6 phi_mud_0 = 0.614 # Kominz et al. 2011 >90% mud curve
7 tops_new = decompact(tops,lith,0.0,phi_sand_0,phi_mud_0,c_sand,c_mud) # c
8  
9 plot_decompaction(tops,tops_new)

https://hinderedsettling.com/2017/04/12/exploring-decompaction-with-python/ 6/6

You might also like