Anthony Jimenez
29 August 2021
http://www.fa-jimenez.com/
This jupyter notebook is based on the published SPE 12777 paper by Bourdet, D., Ayoub, J.A., and Pirard, Y.M.
Using Table 1 of the paper, corresponding to Buildup 2, the pressure change is plotted and the compute_pressure_derivative() function is utilized to output a smoothened, 3-point pressure derivative array.
With this "de-noised" pressure derivative, flow regime analysis is more easily applicable. Further, it is noticed that there are potentially dual porosity effects visible. In future articles, these flow regimes will be analyzed using superposition time and pseudo-steady state dual porosity models.
import pandas as pd
import numpy as np
from scipy.optimize import differential_evolution
import matplotlib.pyplot as plt
%matplotlib inline
# Load in data
df_url = r'https://raw.githubusercontent.com/ajmz1/DCA_optimizer/master/bourdet-buildup-2.csv'
df = pd.read_csv(df_url, skipfooter=1)
df.head()
C:\ProgramData\Anaconda3\lib\site-packages\pandas\util\_decorators.py:311: ParserWarning: Falling back to the 'python' engine because the 'c' engine does not support skipfooter; you can avoid this warning by specifying engine='python'. return func(*args, **kwargs)
dt_hr | dp_psi | pD_0.0 | pD_0.1 | ts_hr | |
---|---|---|---|---|---|
0 | 0.00417 | 0.57 | 4.67619 | 4.67619 | -8.21072 |
1 | 0.00833 | 3.81 | 5.99244 | 5.99244 | -7.51785 |
2 | 0.01250 | 6.55 | 9.88966 | 9.88966 | -7.11265 |
3 | 0.01667 | 10.03 | 13.47654 | 13.47654 | -6.82524 |
4 | 0.02083 | 13.27 | 17.11777 | 17.11777 | -6.60237 |
data = [df.iloc[:,0], df.iloc[:,1]]
def compute_pressure_derivative(L, data):
# Extract the time (x) and the pressure change data (y)
x = np.array(data[0])
y = np.array(data[1])
# This is the x_right_point to use for end point analysis
x_right_endpoint = np.where(abs(np.log10(x) - np.log10(x[-1])) < L)[0][0] - 1
dx_right = np.nan
dy_right = np.nan
# Initialize zeros array before populating it through loop
pressure_der = np.zeros(len(x))
for i in range(len(x)):
# Do not include endpoints in derivative calculations
if (i == 0) or (i == len(x)-1):
pressure_der[i] = np.nan
else:
# Compute maximum steps left and right possible
x_left_max = i
x_right_max = len(x)-i-1
# Main loop for finding the appropriate distance
idx = 1
x_left_found = False
x_right_found = False
while idx < min(x_left_max, x_right_max):
# Assign temporary test points for computing derivative
x_left_temp = x[i-idx]
x_right_temp = x[i+idx]
# Compute distance between neighbor data points and reference point
x_left_dist = abs(np.log10(x[i]) - np.log10(x_left_temp))
x_right_dist = abs(np.log10(x_right_temp) - np.log10(x[i]))
# Assign index variable for later calling the data point value
x_left_idx = i - idx
x_right_idx = i + idx
# Check if we meet length distance
if (x_left_dist > L) or (idx >= x_left_max):
x_left_found = True
if (x_right_dist > L) or (idx >= x_right_max):
x_right_found = True
# Compute the pressure derivative if the distance requirements are met
if ((x_left_found == True) and (x_right_found == True)) or ((idx+1) >= min(x_left_max, x_right_max)):
dy_left = abs(y[i] - y[x_left_idx])
dy_right = abs(y[x_right_idx] - y[i])
dx_left = abs(np.log(x[i]) - np.log(x[x_left_idx]))
dx_right = abs(np.log(x[x_right_idx]) - np.log(x[i]))
pressure_der[i] = ((dy_left / dx_left) * dx_right + (dy_right / dx_right) * dx_left) / \
(dx_left + dx_right)
# Exit the inner loop if successfully computing the derivative
break
else:
idx += 1
return pressure_der
# Compute the pressure derivative
out = compute_pressure_derivative(0.1, data)
# Plot the pressure and presure derivative series
fig, ax = plt.subplots(figsize=[16,8])
ax.scatter(df['dt_hr'], df['dp_psi'], s=50, facecolors='None', edgecolors='k', label=r'$\Delta p$')
ax.scatter(df['dt_hr'], out, s=50, facecolors='None', edgecolors='red', label=r'''$\Delta p'$''')
# Label plot axes
ax.set_xlabel(r'Elapsed Time $\Delta t$')
ax.set_ylabel(r'Pressure Change, $\Delta p$' + '\n' + r'Pressure Change Derivative, $\Delta p_D$')
# Overlay flow regime slope-lines and transitionary flow lines
ax.axline((1.41, 1.0e3), slope=1, color='blue', label='WBS')
ax.axvline(0.3, color='k', ls='-.', label='Transition')
ax.axvline(8.0, color='k', ls='-.', label='')
ax.axvline(27.0, color='k', ls='-.', label='')
ax.text(0.025, 3.5, 'WBS Domination', backgroundcolor='white', fontsize='large', fontweight='bold')
ax.text(0.45, 1.8, 'WBS to Reservoir Flow', backgroundcolor='white', fontsize='large', fontweight='bold')
ax.text(5, 3.5, 'Possible Dual Porosity', backgroundcolor='white', fontsize='large', fontweight='bold')
# Label the plot title
plt.title('Diagnostic Plot for Flow Regime Analysis [Log-Log]')
# Adjust log axes style and limits
ax.set_xlim(1e-3, 1e3)
ax.set_ylim(1e0, 1e3)
ax.set_xscale('log')
ax.set_yscale('log')
ax.set_aspect('equal')
ax.grid(which='major', axis='both', alpha=1, color='k')
ax.grid(which='minor', axis='both', linestyle=':', alpha=1, color='grey')
plt.legend(fontsize='large', framealpha=1, edgecolor='k')
<matplotlib.legend.Legend at 0x1e63db83c70>