11
11
import matplotlib .patches as patches
12
12
import numpy as np
13
13
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 )
56
14
57
15
#########################################
58
16
############### Load files ##############
@@ -206,23 +164,21 @@ def detrend_fold_profiles(fold):
206
164
##################################
207
165
######### Forward model ##########
208
166
##################################
167
+ ## First we make forward models. We use the Southland horizon just
168
+ ## below the forced fold, as it shows no signs of erosion.
209
169
greatest_sill_vel = 7000
210
170
least_sill_vel = 4500
211
171
greatest_phi0 = 0.7
212
- least_phi0 = 0.25
172
+ least_phi0 = 0.20
213
173
greatest_lambda = 3.7
214
174
least_lambda = 1.4
215
175
216
176
## 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 )
218
177
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 )
220
178
greatest_southland_detrended = detrend_fold_profiles (greatest_southland_decompacted )
221
179
222
180
## 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 )
224
181
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 )
226
182
least_southland_detrended = detrend_fold_profiles (least_southland_decompacted )
227
183
228
184
## Make upper limit of sill thickness
@@ -232,7 +188,6 @@ def detrend_fold_profiles(fold):
232
188
least_sill_amp_from_time = time_sill_amp_extraction_2d (top_sill_time , base_sill_time , sill_vel = least_sill_vel )
233
189
234
190
## 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' )
236
191
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' )
237
192
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' )
238
193
plt .ylabel ("Sill thickness / Fold amplitude (meters)" )
@@ -246,22 +201,24 @@ def detrend_fold_profiles(fold):
246
201
##################################
247
202
######### Inverse model ##########
248
203
##################################
204
+ ## Secondly, we solve the inverse problem to find the best fitting decompaction
205
+ ## parameters to fit the sill.
249
206
250
207
## Set vars
251
208
greatest_sill_vel = 7000
252
209
least_sill_vel = 4500
253
210
greatest_phi0 = 0.7
254
- least_phi0 = 0.25
255
- phi0_decimation = 0.05
211
+ least_phi0 = 0.20
212
+ phi0_decimation = 0.01
256
213
greatest_lambda = 3.7
257
214
least_lambda = 1.4
258
- lambda_decimation = 0.1
215
+ lambda_decimation = 0.05
259
216
phi0_vec = np .arange (start = least_phi0 , stop = greatest_phi0 , step = phi0_decimation )
260
217
lambda_vec = np .arange (start = least_lambda , stop = greatest_lambda , step = lambda_decimation )
261
218
grid_search = np .zeros ((len (phi0_vec ), len (lambda_vec )))
262
219
263
220
264
- ## Misfit function
221
+ ## Misfit functions
265
222
def misfit (sill , forced_fold ):
266
223
'''
267
224
Function which returns the misfit between a sill curve and a
@@ -272,54 +229,172 @@ def misfit(sill, forced_fold):
272
229
if sill ['trace' ][i ] in forced_fold ['trace' ].values :
273
230
total_misfit += (sill ['sill_amp_meters' ][i ]/ 1000.0 ) - forced_fold ['decompacted_profile_detrended_km' ].loc [forced_fold ['trace' ]== sill ['trace' ][i ]].values
274
231
return total_misfit
275
-
276
232
#misfit(sill_amp_from_time, fold_detrended)
277
233
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
+
278
250
## Grid search
279
251
## Outer loop - phi0
252
+ sill_amp_gs = time_sill_amp_extraction_2d (top_sill_time , base_sill_time , sill_vel = 6500 )
280
253
for i_loc , i in enumerate (phi0_vec ):
281
254
## Inner loop - lambda
282
255
for j_loc , j in enumerate (lambda_vec ):
283
256
## 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.
287
259
print ('i = ' , i_loc , ', j = ' , j_loc , ', total = ' , j_loc + (len (lambda_vec )* i_loc ), 'out of ' , len (phi0_vec )* len (lambda_vec ))
288
260
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
293
264
max_loc = np .where (g2 == g2 .min ())
294
265
opt_phi0 = phi0_vec [::- 1 ][max_loc [0 ]]
295
266
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 ])
298
271
#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' )
300
274
plt .ylabel (r'$\Phi_0$' )
301
275
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')
303
277
plt .show ()
304
278
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 ()
309
289
310
290
## Plot fold profile with minimum misfit
311
- fold_decompacted = decompact_fold (top_forced_fold_clip , tolerance = 0.0001 , phi0 = opt_phi0 , Lambda = opt_lambda )
312
291
southland_decompacted = decompact_fold (southland_clip , tolerance = 0.0001 , phi0 = opt_phi0 , Lambda = opt_lambda )
313
-
314
- fold_detrended = detrend_fold_profiles (fold_decompacted )
315
292
southland_detrended = detrend_fold_profiles (southland_decompacted )
316
-
317
- plt .plot (fold_detrended ['trace' ], fold_detrended ['decompacted_profile_detrended_km' ]* 1000 , label = 'Decompacted fold' )
318
293
plt .plot (southland_detrended ['trace' ], southland_detrended ['decompacted_profile_detrended_km' ]* 1000 , label = 'Decompacted Southland' )
319
294
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' )
320
295
plt .ylabel ("Sill thickness / Fold amplitude (meters)" )
321
296
plt .xlabel ("Trace" )
322
297
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' )
324
299
plt .show ()
325
300
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