Skip to content

Commit 9973499

Browse files
committed
Almost finished Resolution stuff. misfit for functions finished and debugged, plots made, etc.
1 parent 1827e53 commit 9973499

File tree

5 files changed

+149
-74
lines changed

5 files changed

+149
-74
lines changed

Resolution_forward_model.pdf

3.5 KB
Binary file not shown.

Resolution_inverse_grid.pdf

15.9 KB
Binary file not shown.

Resolution_inverse_model.pdf

1.24 KB
Binary file not shown.

Resolution_optimum_inverse_model.pdf

17.2 KB
Binary file not shown.

resolution_1/resolution_1_4_forward_inverse.py

Lines changed: 149 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -11,48 +11,6 @@
1111
import matplotlib.patches as patches
1212
import numpy as np
1313

14-
## Read in wireline log data from .las files
15-
log_data = pd.read_csv("Wireline/resolution-1_final.las", delim_whitespace=True,
16-
header=None, names=['DEPTH', 'BS', 'CALI', 'DTC', 'GR', 'GR_CORR',
17-
'RESD', 'RESS', 'SP'], na_values=-999.2500, skiprows=56)
18-
19-
## Keep a copy incase of need to check unaltered params.
20-
log_data_original = log_data.copy()
21-
22-
## Fill log data with dummy values for water and sed. Create a dataframe of the filled values,
23-
## then concat to top of log_data.
24-
# Water depth is looked up from paper logs
25-
water_depth = 64.0 # meters
26-
# Get the first recorded depth in the log (note shallow unconsolidate section rarely logged)
27-
first_depth_in_well = log_data['DEPTH'][0]
28-
# Find the depth sampling interval
29-
depth_sample_interval = np.round(log_data['DEPTH'][1] - log_data['DEPTH'][0], decimals=4)
30-
# Create a vector of depths to fill
31-
depths = np.arange(start=0,stop=first_depth_in_well, step=depth_sample_interval)
32-
# Create a vector of bit sizes
33-
BS = np.repeat(log_data['BS'][0],len(depths))
34-
# Create a vector of NaNs for logs not used in analysis.
35-
CALI= np.repeat(np.nan,len(depths))
36-
GR= np.repeat(np.nan,len(depths))
37-
GR_CORR= np.repeat(np.nan,len(depths))
38-
RESD= np.repeat(np.nan,len(depths))
39-
RESS= np.repeat(np.nan,len(depths))
40-
SP= np.repeat(np.nan,len(depths))
41-
# Create two vectors of velocity in the water and shalow sed, and concat.
42-
DTC_water = np.repeat(1/(1500*3.28084/1e6),water_depth)
43-
DTC_sed = np.repeat(1/(1800*3.28084/1e6),len(depths)-water_depth)
44-
DTC=np.insert(DTC_sed, 0, DTC_water)
45-
# Make a temp dataframe of the newly generated data and NaNs, and then concat to make the full data.
46-
temp_dataframe = pd.DataFrame({'DEPTH':depths, 'BS':BS, 'CALI':CALI, 'DTC':DTC, 'GR':GR, 'GR_CORR':GR_CORR, 'RESD':RESD, 'RESS':RESS, 'SP':SP})
47-
log_data = pd.concat((temp_dataframe,log_data), ignore_index=True)
48-
49-
# Interpolate missing values in the logs (different tools turned on at different times) to enable the cumulative integration
50-
log_data.loc[0,'DT_s_per_m'] = (1.0/1500)
51-
log_data['DT_s_per_m'] = log_data['DT_s_per_m'].interpolate()
52-
log_data['DTC'] = log_data['DTC'].interpolate()
53-
54-
# Make a Vp log, to plot and also to later find the sill velocity from.
55-
log_data['VP_m_per_s'] = 1/(log_data['DTC'] * (1e-6) / 0.3048)
5614

5715
#########################################
5816
############### Load files ##############
@@ -206,23 +164,21 @@ def detrend_fold_profiles(fold):
206164
##################################
207165
######### Forward model ##########
208166
##################################
167+
## First we make forward models. We use the Southland horizon just
168+
## below the forced fold, as it shows no signs of erosion.
209169
greatest_sill_vel = 7000
210170
least_sill_vel = 4500
211171
greatest_phi0 = 0.7
212-
least_phi0=0.25
172+
least_phi0=0.20
213173
greatest_lambda = 3.7
214174
least_lambda = 1.4
215175

216176
## Make upper limit of fold profile
217-
greatest_fold_decompacted = decompact_fold(top_forced_fold_clip, tolerance=0.0001, phi0=greatest_phi0, Lambda=greatest_lambda)
218177
greatest_southland_decompacted = decompact_fold(southland_clip, tolerance=0.0001, phi0=greatest_phi0, Lambda=greatest_lambda)
219-
greatest_fold_detrended = detrend_fold_profiles(greatest_fold_decompacted)
220178
greatest_southland_detrended = detrend_fold_profiles(greatest_southland_decompacted)
221179

222180
## Make lower limit of fold profile
223-
least_fold_decompacted = decompact_fold(top_forced_fold_clip, tolerance=0.0001, phi0=least_phi0, Lambda=least_lambda)
224181
least_southland_decompacted = decompact_fold(southland_clip, tolerance=0.0001, phi0=least_phi0, Lambda=least_lambda)
225-
least_fold_detrended = detrend_fold_profiles(least_fold_decompacted)
226182
least_southland_detrended = detrend_fold_profiles(least_southland_decompacted)
227183

228184
## Make upper limit of sill thickness
@@ -232,7 +188,6 @@ def detrend_fold_profiles(fold):
232188
least_sill_amp_from_time = time_sill_amp_extraction_2d(top_sill_time, base_sill_time, sill_vel=least_sill_vel)
233189

234190
## Plot
235-
plt.fill_between(greatest_fold_detrended['trace'], least_fold_detrended['decompacted_profile_detrended_km']*1000, greatest_fold_detrended['decompacted_profile_detrended_km']*1000, color='b', alpha=0.2, label='Decompacted fold')
236191
plt.fill_between(greatest_southland_detrended['trace'], least_southland_detrended['decompacted_profile_detrended_km']*1000, greatest_southland_detrended['decompacted_profile_detrended_km']*1000, color='g', alpha=0.2, label='Decompacted Southland')
237192
plt.fill_between(greatest_sill_amp_from_time['trace'], least_sill_amp_from_time['sill_amp_meters'], greatest_sill_amp_from_time['sill_amp_meters'], color='r', alpha=0.2, label='Sill thickness')
238193
plt.ylabel("Sill thickness / Fold amplitude (meters)")
@@ -246,22 +201,24 @@ def detrend_fold_profiles(fold):
246201
##################################
247202
######### Inverse model ##########
248203
##################################
204+
## Secondly, we solve the inverse problem to find the best fitting decompaction
205+
## parameters to fit the sill.
249206

250207
## Set vars
251208
greatest_sill_vel = 7000
252209
least_sill_vel = 4500
253210
greatest_phi0 =0.7
254-
least_phi0=0.25
255-
phi0_decimation = 0.05
211+
least_phi0=0.20
212+
phi0_decimation = 0.01
256213
greatest_lambda = 3.7
257214
least_lambda = 1.4
258-
lambda_decimation = 0.1
215+
lambda_decimation = 0.05
259216
phi0_vec = np.arange(start=least_phi0, stop=greatest_phi0, step=phi0_decimation)
260217
lambda_vec = np.arange(start=least_lambda, stop=greatest_lambda, step=lambda_decimation)
261218
grid_search = np.zeros((len(phi0_vec), len(lambda_vec)))
262219

263220

264-
## Misfit function
221+
## Misfit functions
265222
def misfit(sill, forced_fold):
266223
'''
267224
Function which returns the misfit between a sill curve and a
@@ -272,54 +229,172 @@ def misfit(sill, forced_fold):
272229
if sill['trace'][i] in forced_fold['trace'].values:
273230
total_misfit += (sill['sill_amp_meters'][i]/1000.0) - forced_fold['decompacted_profile_detrended_km'].loc[forced_fold['trace']==sill['trace'][i]].values
274231
return total_misfit
275-
276232
#misfit(sill_amp_from_time, fold_detrended)
277233

234+
## Misfit function
235+
def RMS_misfit(sill, forced_fold):
236+
'''
237+
Function which returns the misfit between a sill curve and a
238+
forced fold curve.
239+
'''
240+
total_misfit = 0
241+
counter = 0
242+
for i in range(len(sill)):
243+
if sill['trace'][i] in forced_fold['trace'].values:
244+
counter += 1
245+
total_misfit += np.power((sill['sill_amp_meters'][i]/1000.0) - forced_fold['decompacted_profile_detrended_km'].loc[forced_fold['trace']==sill['trace'][i]].values, 2)
246+
rms_misfit = np.power((total_misfit / counter), 0.5)
247+
return rms_misfit
248+
RMS_misfit(sill_amp_from_time, fold_detrended)
249+
278250
## Grid search
279251
## Outer loop - phi0
252+
sill_amp_gs = time_sill_amp_extraction_2d(top_sill_time, base_sill_time, sill_vel=6500)
280253
for i_loc, i in enumerate(phi0_vec):
281254
## Inner loop - lambda
282255
for j_loc, j in enumerate(lambda_vec):
283256
## Note, could do a third loop for sill vel
284-
sill_amp_gs = time_sill_amp_extraction_2d(top_sill_time, base_sill_time, sill_vel=5500)
285-
fold_gs = detrend_fold_profiles(decompact_fold(top_forced_fold_clip, tolerance=0.0001, phi0=i, Lambda=j))
286-
grid_search[i_loc,j_loc] = misfit(sill_amp_gs, fold_gs) ## fill this with the misfit function.
257+
fold_gs = detrend_fold_profiles(decompact_fold(southland_clip, tolerance=0.0001, phi0=i, Lambda=j))
258+
grid_search[len(phi0_vec)-(i_loc+1),j_loc] = RMS_misfit(sill_amp_gs, fold_gs) ## fill this with the misfit function.
287259
print('i = ', i_loc, ', j = ', j_loc, ', total = ', j_loc+(len(lambda_vec)*i_loc), 'out of ', len(phi0_vec)*len(lambda_vec))
288260

289-
290-
291-
## Plot the grid searc
292-
g2 = abs(grid_search)
261+
## Find the optimum values
262+
#g2 = abs(grid_search)
263+
g2 = grid_search
293264
max_loc = np.where(g2 == g2.min())
294265
opt_phi0 = phi0_vec[::-1][max_loc[0]]
295266
opt_lambda = lambda_vec[max_loc[1]]
296-
plt.figure()
297-
plt.imshow(g2, aspect='equal', cmap='jet_r', extent=[least_lambda, greatest_lambda, least_phi0, greatest_phi0])
267+
268+
## Plot the grid searc
269+
plt.figure(figsize=(4,3))
270+
plt.imshow(g2, aspect='auto', cmap='jet_r', extent=[least_lambda, greatest_lambda, least_phi0, greatest_phi0])
298271
#plt.scatter(y=[opt_phi0], x=[opt_lambda], c='r', s=40)
299-
plt.colorbar(cmap='jet_r')
272+
cbar = plt.colorbar(cmap='jet_r')
273+
cbar.ax.set_ylabel('RMS Misfit')
300274
plt.ylabel(r'$\Phi_0$')
301275
plt.xlabel(r'$\lambda$')
302-
plt.savefig("/home/murray/Documents/thesis_python_code/Resolution_inverse_grid.pdf",bbox_inches='tight')
276+
#plt.savefig("/home/murray/Documents/thesis_python_code/Resolution_inverse_grid.pdf",bbox_inches='tight')
303277
plt.show()
304278

305-
## Next to do - change code so the white padding at the base of the image goes.
306-
# add contours
307-
# replot with more squares
308-
# find why lambda axes doesn't go to lambda max (def a bug somewhere)
279+
plt.figure(figsize=(5,4))
280+
plt.contour(g2, 20, linewidths=0.5, colors='k', aspect='auto', extent=[least_lambda, greatest_lambda, greatest_phi0, least_phi0])
281+
plt.contourf(g2, 20, aspect='auto', cmap='jet_r', extent=[least_lambda, greatest_lambda, greatest_phi0, least_phi0])
282+
cbar = plt.colorbar(cmap='jet_r')
283+
cbar.ax.set_ylabel('RMS Misfit')
284+
plt.scatter(opt_lambda, opt_phi0, marker='o', s=100, c='k')
285+
plt.ylabel(r'$\Phi_0$')
286+
plt.xlabel(r'$\lambda$')
287+
#plt.savefig("/home/murray/Documents/thesis_python_code/Resolution_inverse_grid.pdf",bbox_inches='tight')
288+
plt.show()
309289

310290
## Plot fold profile with minimum misfit
311-
fold_decompacted = decompact_fold(top_forced_fold_clip, tolerance=0.0001, phi0=opt_phi0, Lambda=opt_lambda)
312291
southland_decompacted = decompact_fold(southland_clip, tolerance=0.0001, phi0=opt_phi0, Lambda=opt_lambda)
313-
314-
fold_detrended = detrend_fold_profiles(fold_decompacted)
315292
southland_detrended = detrend_fold_profiles(southland_decompacted)
316-
317-
plt.plot(fold_detrended['trace'], fold_detrended['decompacted_profile_detrended_km']*1000, label='Decompacted fold')
318293
plt.plot(southland_detrended['trace'], southland_detrended['decompacted_profile_detrended_km']*1000, label='Decompacted Southland')
319294
plt.fill_between(greatest_sill_amp_from_time['trace'], least_sill_amp_from_time['sill_amp_meters'], greatest_sill_amp_from_time['sill_amp_meters'], color='r', alpha=0.2, label='Sill thickness')
320295
plt.ylabel("Sill thickness / Fold amplitude (meters)")
321296
plt.xlabel("Trace")
322297
plt.legend(loc=8)
323-
plt.savefig("/home/murray/Documents/thesis_python_code/Resolution_inverse_model.pdf",bbox_inches='tight')
298+
plt.savefig("/home/murray/Documents/thesis_python_code/Resolution_optimum_inverse_model.pdf",bbox_inches='tight')
324299
plt.show()
325300

301+
### Finally, make a theoretical porosity curve from the above results, fit to the
302+
### porosity curve with a 1D gridsearch, and plot the misfit.
303+
304+
### Read in well logs and apply corrections
305+
## Read in wireline log data from .las files
306+
log_data = pd.read_csv("Wireline/resolution-1_final.las", delim_whitespace=True,
307+
header=None, names=['DEPTH', 'BS', 'CALI', 'DTC', 'GR', 'GR_CORR',
308+
'RESD', 'RESS', 'SP'], na_values=-999.2500, skiprows=56)
309+
310+
## Keep a copy incase of need to check unaltered params.
311+
log_data_original = log_data.copy()
312+
313+
## Fill log data with dummy values for water and sed. Create a dataframe of the filled values,
314+
## then concat to top of log_data.
315+
# Water depth is looked up from paper logs
316+
water_depth = 64.0 # meters
317+
# Get the first recorded depth in the log (note shallow unconsolidate section rarely logged)
318+
first_depth_in_well = log_data['DEPTH'][0]
319+
# Find the depth sampling interval
320+
depth_sample_interval = np.round(log_data['DEPTH'][1] - log_data['DEPTH'][0], decimals=4)
321+
# Create a vector of depths to fill
322+
depths = np.arange(start=0,stop=first_depth_in_well, step=depth_sample_interval)
323+
# Create a vector of bit sizes
324+
BS = np.repeat(log_data['BS'][0],len(depths))
325+
# Create a vector of NaNs for logs not used in analysis.
326+
CALI= np.repeat(np.nan,len(depths))
327+
GR= np.repeat(np.nan,len(depths))
328+
GR_CORR= np.repeat(np.nan,len(depths))
329+
RESD= np.repeat(np.nan,len(depths))
330+
RESS= np.repeat(np.nan,len(depths))
331+
SP= np.repeat(np.nan,len(depths))
332+
# Create two vectors of velocity in the water and shalow sed, and concat.
333+
DTC_water = np.repeat(1/(1500*3.28084/1e6),water_depth)
334+
DTC_sed = np.repeat(1/(1800*3.28084/1e6),len(depths)-water_depth)
335+
DTC=np.insert(DTC_sed, 0, DTC_water)
336+
# Make a temp dataframe of the newly generated data and NaNs, and then concat to make the full data.
337+
temp_dataframe = pd.DataFrame({'DEPTH':depths, 'BS':BS, 'CALI':CALI, 'DTC':DTC, 'GR':GR, 'GR_CORR':GR_CORR, 'RESD':RESD, 'RESS':RESS, 'SP':SP})
338+
log_data = pd.concat((temp_dataframe,log_data), ignore_index=True)
339+
340+
# Interpolate missing values in the logs (different tools turned on at different times) to enable the cumulative integration
341+
log_data.loc[0,'DT_s_per_m'] = (1.0/1500)
342+
log_data['DT_s_per_m'] = log_data['DT_s_per_m'].interpolate()
343+
log_data['DTC'] = log_data['DTC'].interpolate()
344+
345+
# Get array of depths
346+
log_depths = log_data['DEPTH'].values/1000.00
347+
348+
# Apply formula to find the poro at those depths
349+
theoretical_poro = opt_phi0 * np.exp(-(log_depths) / opt_lambda)
350+
#plt.figure(figsize=(2,4))
351+
#plt.plot(theoretical_poro, log_depths)
352+
#plt.gca().invert_yaxis()
353+
#plt.xlim(0,0.6)
354+
#plt.show()
355+
356+
# Define RHG function to change sonic to poro
357+
def RHG(well_log, matrix_vel):
358+
'''Function to calculate Raymer-Hunt-Gardner porosity from Sonic log'''
359+
return (5.0/8)*((log_data['DTC'] - matrix_vel)/(log_data['DTC']))
360+
361+
## Define misfit function for RHG vs. theoretical poro
362+
def RHG_misfit(log_poro, theoretical_poro, range_of_interest=(2145, 12100)):
363+
'''Function to calculate the misfit between a theoretical porosity log and a calculated
364+
porosity log '''
365+
misfit_vec = np.power((log_poro[range_of_interest[0]:range_of_interest[1]] - theoretical_poro[range_of_interest[0]:range_of_interest[1]]), 2)
366+
return np.sqrt(np.divide(np.sum(misfit_vec), len(misfit_vec)))
367+
#RHG_misfit(rhg, theoretical_poro)
368+
369+
## Plot test curves of theoretical poro and cal poro, to check everything
370+
rhg = RHG(log_data, 55)
371+
plt.figure(figsize=(4,6))
372+
plt.plot(theoretical_poro, log_depths, label='Theoretical poro')
373+
plt.plot(rhg, log_depths, label='Calc poro')
374+
plt.plot(rhg[2145:12100], log_depths[2145:12100], label='Fitted region')
375+
plt.legend()
376+
plt.xlabel(r"$\phi$")
377+
plt.ylabel('Depth, km')
378+
plt.gca().invert_yaxis()
379+
plt.xlim(0,0.6)
380+
plt.show()
381+
382+
## Define range of matrix velocities
383+
matrix_vels = np.arange(start=40, stop=80, step=1)
384+
385+
## Define vector of zeroes for misfit results
386+
misfits = np.zeros(len(matrix_vels))
387+
388+
## Loop over matrix vels to find the misfit for the matrix velocities
389+
for i_loc, i in enumerate(matrix_vels):
390+
misfits[i_loc] = RHG_misfit(RHG(log_data, i), theoretical_poro, range_of_interest=(0, 12100))
391+
392+
plt.plot(matrix_vels, abs(misfits))
393+
plt.axvline(x=matrix_vels[np.argmin(misfits)], c='r')
394+
plt.text(54, 0.1, r'$Minimum = 52 \mu s / ft $', color='r')
395+
plt.ylabel("RMSE misfit")
396+
plt.xlabel(r"$Matrix \ velocity \ (\mu s / ft)$")
397+
plt.show()
398+
399+
400+
# Plot curve of misfit vs matrix sonic velocity, and add static ranges for sensible velcities

0 commit comments

Comments
 (0)