def test_load_selected_intervals(self): these_intervals = ['Sand E', 'Sand F'] #these_intervals = ['Sand F'] wp = Project(name='MyProject', log_to_stdout=True) wells = wp.load_all_wells(include_these_intervals=these_intervals) for _, well in wells.items(): md = well.block[def_lb_name].get_md() print(well.well, md.min(), md.max()) with self.subTest(): self.assertTrue(True)
def test_load_selected_wells(self): """ For this test to work, the listed wells below need to have "use = Yes" in the project table """ these_wells = ['WELL_A', 'WELL_B'] wp = Project(name='MyProject', log_to_stdout=True) wells = wp.load_all_wells(include_these_wells=these_wells) for wname, _ in wells.items(): with self.subTest(): self.assertTrue(wname in these_wells) with self.subTest(): self.assertFalse(wname == 'WELL_C')
def test_collect_data(self): wp = Project(name='MyProject', log_to_stdout=True) wells = wp.load_all_wells() wis = wp.load_all_wis() log_table = {'Porosity': 'phie', 'S velocity': 'vs'} results, results_per_well, depth_from_top = uc.create_containers( list(log_table.values())) interval_names = [ 'Sand C', 'Shale C', 'Sand D', 'Sand E', 'Sand F', 'Shale G', 'Sand H' ] for wi_name in interval_names: uc.collect_data_for_this_interval(wells, list(log_table.values()), wis, wi_name, results, results_per_well, depth_from_top, {}, log_table) with self.subTest(): self.assertTrue(True)
def main(): # Create a project wp = Project( name='FluidSub', project_table='excels/project_table.xlsx') # Load wells wells = wp.load_all_wells(block_name='FBlock') w = wells['WELL_F'] # Load working intervals and templates wis = uio.project_working_intervals(wp.project_table) templates = uio.project_templates(wp.project_table) # Load fluids myfluids = FluidMix() myfluids.read_excel(wp.project_table) print(myfluids.print_all_fluids()) # Load minerals mymins = MineralMix() mymins.read_excel(wp.project_table) print(mymins.print_all_minerals()) # Fluid substitution cutoffs = {'Volume': ['<', 0.5], 'Porosity': ['>', 0.1]} log_table = {'P velocity': 'vp_dry', 'S velocity': 'vs_dry', 'Density': 'rho_dry', 'Porosity': 'phie', 'Volume': 'vcl'} tag = 'fs' # tag the resulting logs after fluid substitution rp.run_fluid_sub(wells, log_table, mymins, myfluids, cutoffs, wis, tag, templates=templates, block_name='FBlock') # plot results fig, ax = plt.subplots(figsize=(10, 8)) log_table = {'P velocity': 'vp_dry', 'S velocity': 'vs_dry', 'Density': 'rho_dry', 'Porosity': 'phie', 'Volume': 'vcl'} templates['WELL_F']['color'] = 'b' prp.plot_rp(wells, log_table, wis, 'SAND E', cutoffs, templates, fig=fig, ax=ax, edge_color=False, show_masked=True, block_name='FBlock') # specify the new logs coming from the fluid substitution log_table = {'P velocity': 'vp_dry_fs', 'S velocity': 'vs_dry_fs', 'Density': 'rho_dry_fs', 'Porosity': 'phie', 'Volume': 'vcl'} templates['WELL_F']['color'] = 'r' prp.plot_rp(wells, log_table, wis, 'SAND E', cutoffs, templates, fig=fig, ax=ax, edge_color=False, block_name='FBlock') # specify the new logs coming from the RokDoc fluid substitution stored in the las file log_table = {'P velocity': 'vp_so08', 'S velocity': 'vs_so08', 'Density': 'rho_so08', 'Porosity': 'phie', 'Volume': 'vcl'} templates['WELL_F']['color'] = 'g' prp.plot_rp(wells, log_table, wis, 'SAND E', cutoffs, templates, fig=fig, ax=ax, edge_color=False, block_name='FBlock') del(wells['WELL_F'].block['FBlock'].logs['rho_so08']) del(wells['WELL_F'].block['FBlock'].logs['rho_sg08']) wells['WELL_F'].depth_plot('Density') # Save results for well in wells.values(): uio.write_las( os.path.join(wp.working_dir, 'results_folder', '{}.las'.format(well.well)), well.header, well.block['FBlock'].header, well.block['FBlock'].logs ) plt.show()
def test_synt2(): """ This essentially tries to copy test_synt(), which is based on https://github.com/seg/tutorials-2014/blob/master/1406_Make_a_synthetic/how_to_make_synthetic.ipynb but using blixt_rp built in functionality instead :return: """ import blixt_utils.io.io as uio import plotting.plot_logs as ppl from core.well import Project from core.well import Well import blixt_utils.misc.convert_data as ucd wp = Project() wells = wp.load_all_wells() w = wells[list(wells.keys())[0]] # take first well wis = uio.project_working_intervals(wp.project_table) log_table = { 'P velocity': 'vp_dry', 'S velocity': 'vs_dry', 'Density': 'rho_dry' } # when input is in feet and usec #depth = ucd.convert(w.block['Logs'].logs['depth'].data, 'ft', 'm') #rho_orig = w.block['Logs'].logs['rhob'].data * 1000. # g/cm3 to kg/m3 #vp_orig = ucd.convert(w.block['Logs'].logs['dt'].data, 'us/ft', 'm/s') #dt_orig = w.block['Logs'].logs['dt'].data * 3.2804 # convert usec/ft to usec/m # else depth = w.block['Logs'].logs['depth'].data rho_orig = w.block['Logs'].logs[ log_table['Density']].data * 1000. # g/cm3 to kg/m3 vp_orig = w.block['Logs'].logs[log_table['P velocity']].data vs_orig = w.block['Logs'].logs[log_table['S velocity']].data # when input is in feet and usec #rho = w.block['Logs'].logs['rhob'].despike(0.1) * 1000. # g/cm3 to kg/m3 #vp = ucd.convert(w.block['Logs'].logs['dt'].despike(5), 'us/ft', 'm/s') #dt = w.block['Logs'].logs['dt'].despike(5) * 3.2804 # convert usec/ft to usec/m # else rho = w.block['Logs'].logs[log_table['Density']].despike( 0.1) * 1000. # g/cm3 to kg/m3 vp = w.block['Logs'].logs[log_table['P velocity']].despike(200) vs = w.block['Logs'].logs[log_table['S velocity']].despike(200) start = 13000 end = 14500 # Plot despiking results plot = False if plot: fig, axes = plt.subplots(3, 1, sharex=True, figsize=(18, 12)) for i, y1, y2 in zip([0, 1, 2], [rho_orig, vp_orig, vs_orig], [rho, vp, vs]): axes[i].plot(depth[start:end], y2[start:end], 'y', lw=3) axes[i].plot(depth[start:end], y1[start:end], 'k', lw=0.5) axes[i].legend(['Smooth & despiked', 'Original']) tdr = w.time_to_depth(log_table['P velocity'], debug=False) r0 = rp.intercept(vp, None, rho, None, along_wiggle=True) plot = True
def test_synt(): """ https://github.com/seg/tutorials-2014/blob/master/1406_Make_a_synthetic/how_to_make_synthetic.ipynb :return: """ import blixt_utils.io.io as uio import plotting.plot_logs as ppl from core.well import Project from core.well import Well import blixt_utils.misc.convert_data as ucd wp = Project() well_table = { os.path.join(wp.working_dir, 'test_data/L-30.las'): { 'Given well name': 'WELL_L', 'logs': { 'dt': 'Sonic', 'cald': 'Caliper', 'cals': 'Caliper', 'rhob': 'Density', 'grd': 'Gamma ray', 'grs': 'Gamma ray', 'ild': 'Resistivity', 'ilm': 'Resistivity', 'll8': 'Resistivity', 'nphils': 'Neutron density' }, 'Note': 'Some notes for well A' } } wis = uio.project_working_intervals(wp.project_table) w = Well() w.read_well_table(well_table, 0, block_name='Logs') depth = w.block['Logs'].logs['depth'].data / 3.28084 # feet to m rho_orig = w.block['Logs'].logs['rhob'].data * 1000. # g/cm3 to kg/m3 vp_orig = ucd.convert(w.block['Logs'].logs['dt'].data, 'us/ft', 'm/s') dt_orig = w.block['Logs'].logs[ 'dt'].data * 3.2804 # convert usec/ft to usec/m # # Start of copying the notebook results: # https://github.com/seg/tutorials-2014/blob/master/1406_Make_a_synthetic/how_to_make_synthetic.ipynb # def f2m(item_in_feet): "converts feet to meters" try: return item_in_feet / 3.28084 except TypeError: return float(item_in_feet) / 3.28084 kb = f2m(w.header.kb.value) water_depth = f2m(w.header.gl.value) # has a negative value #top_of_log = f2m(w.block['Logs'].header.strt.value) top_of_log = f2m(1150.5000) # The DT log starts at this value repl_int = top_of_log - kb + water_depth water_vel = 1480 # m/s EGL_time = 2.0 * np.abs(kb) / water_vel #water_twt = 2.0 * abs(water_depth + EGL_time) / water_vel # ORIG THIS SEEMS LIKE MIXING distance with time! water_twt = 2.0 * abs(water_depth + np.abs(kb)) / water_vel # My version repl_vel = 1600. # m/s repl_time = 2. * repl_int / repl_vel log_start_time = water_twt + repl_time print('KB elevation: {} [m]'.format(kb)) print('Seafloor elevation: {} [m]'.format(water_depth)) print('Ground level time above SRD: {} [s]'.format(EGL_time)) print('Water time: {} [s]'.format(water_twt)) print('Top of Sonic log: {} [m]'.format(top_of_log)) print('Replacement interval: {} [m]'.format(repl_int)) print('Two-way replacement time: {} [s]'.format(repl_time)) print('Top-of-log starting time: {} [s]'.format(log_start_time)) def tvdss(md): # Assumes a vertical well # md in meter return md - kb def rolling_window(a, window): shape = a.shape[:-1] + (a.shape[-1] - window + 1, window) strides = a.strides + (a.strides[-1], ) rolled = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides) return rolled #plt.figure(figsize=(18, 4)) #plt.plot(depth, rho_sm, 'b', depth, rho, 'k', alpha=0.5) def despike(curve, curve_sm, max_clip): spikes = np.where(curve - curve_sm > max_clip)[0] spukes = np.where(curve_sm - curve > max_clip)[0] out = np.copy(curve) out[spikes] = curve_sm[ spikes] + max_clip # Clip at the max allowed diff out[spukes] = curve_sm[ spukes] - max_clip # Clip at the min allowed diff return out window = 13 # the length of filter is 13 samples or ~ 2 metres # Density rho_sm = np.median(rolling_window(rho_orig, window), -1) rho_sm = np.pad(rho_sm, int(window / 2), mode='edge') rho = despike(rho_orig, rho_sm, max_clip=100) rho_test = w.block['Logs'].logs['rhob'].despike( 0.1) * 1000. # g/cm3 to kg/m3 # dt dt_sm = np.median(rolling_window(dt_orig, window), -1) dt_sm = np.pad(dt_sm, int(window / 2), mode='edge') dt = despike(dt_orig, dt_sm, max_clip=10) # My test of despiking the velocity directly vp_sm = np.median(rolling_window(vp_orig, window), -1) vp_sm = np.pad(vp_sm, int(window / 2), mode='edge') vp = despike(vp_orig, vp_sm, max_clip=200) # Plot result of despiking start = 13000 end = 14500 plot = True if plot: plt.figure(figsize=(18, 4)) plt.plot(depth[start:end], rho_orig[start:end], 'b', lw=3) #plt.plot(depth[start:end], rho_sm[start:end], 'b') plt.plot(depth[start:end], rho[start:end], 'r', lw=2) plt.plot(depth[start:end], rho_test[start:end], 'k--') plt.title('de-spiked density') plt.figure(figsize=(18, 4)) plt.plot(depth[start:end], dt_orig[start:end], 'k') plt.plot(depth[start:end], dt_sm[start:end], 'b') plt.plot(depth[start:end], dt[start:end], 'r') plt.title('de-spiked sonic') #plt.figure(figsize=(18, 4)) #plt.plot(depth[start:end], vp_orig[start:end], 'k') #plt.plot(depth[start:end], vp_sm[start:end], 'b') #plt.plot(depth[start:end], vp[start:end], 'r') #plt.title('de-spiked Vp') # Compute the time-depth relationship # two-way-time to depth relationship scaled_dt = 0.1524 * np.nan_to_num( dt ) / 1.e6 # scale the sonic log by the sample interval (6 inches or 0.1524 m) # and go from usec to sec tcum = 2 * np.cumsum(scaled_dt) # integration tdr = tcum + log_start_time print(tdr[:10], tdr[-10:]) # Compute acoustic impedance ai = (1e6 / dt) * rho # Compute reflection rc = (ai[1:] - ai[:-1]) / (ai[1:] + ai[:-1]) # Compute reflection "my way" r0 = rp.intercept(vp, None, rho, None, along_wiggle=True) # The difference between rc and r0 lies basically in the difference in smoothing and clipping of dt vs. vp plot = False if plot: plt.figure(figsize=(18, 4)) plt.plot(depth[start:end], rc[start:end], 'k') plt.plot(depth[start:end], r0[start:end], 'b') plt.title('Comparison of reflection coefficients') plt.legend(['Notebook way', 'My way']) def find_nearest(array, value): idx = (np.abs(array - value)).argmin() return idx tops = {} for _key in list(wis['WELL_L'].keys()): tops[_key] = wis['WELL_L'][_key][0] tops_twt = {} for _key in list(wis['WELL_L'].keys()): tops_twt[_key] = tdr[find_nearest(depth, wis['WELL_L'][_key][0])] # RESAMPLING FUNCTION t_step = 0.004 max_t = 3.0 t = np.arange(0, max_t, t_step) ai_t = np.interp(x=t, xp=tdr, fp=ai) rc_t = (ai_t[1:] - ai_t[:-1]) / (ai_t[1:] + ai_t[:-1]) # Compute the depth-time relation dtr = np.array([depth[find_nearest(tdr, tt)] for tt in t]) # Define a Ricker wavelet def ricker(_f, _length, _dt): _t = np.linspace(-_length / 2, (_length - _dt) / 2, _length / _dt) _y = (1. - 2. * (np.pi**2) * (_f**2) * (_t**2)) * np.exp(-(np.pi**2) * (_f**2) * (_t**2)) return _t, _y # Do the convolution rc_t = np.nan_to_num(rc_t) tw, w = ricker(_f=25, _length=0.512, _dt=0.004) synth = np.convolve(w, rc_t, mode='same') plot = False if plot: f2 = plt.figure(figsize=[10, 12]) ax1 = f2.add_axes([0.05, 0.1, 0.2, 0.9]) ax1.plot(ai, depth, 'k', alpha=0.75) ax1.set_title('impedance') ax1.set_ylabel('measured depth ' + '$[m]$', fontsize='12') ax1.set_xlabel(r'$kg/m^2s^2$ ', fontsize='16') ax1.set_ylim(0, 4500) ax1.set_xticks([0.0e7, 0.5e7, 1.0e7, 1.5e7, 2.0e7]) ax1.invert_yaxis() ax1.grid() ax2 = f2.add_axes([0.325, 0.1, 0.2, 0.9]) ppl.wiggle_plot(ax2, dtr[:-1], synth, fill='pos') ax2.set_ylim(0, 4500) ax2.invert_yaxis() ax2.grid() ax3 = f2.add_axes([0.675, 0.1, 0.1, 0.9]) ax3.plot(ai_t, t, 'k', alpha=0.75) ax3.set_title('impedance') ax3.set_ylabel('two-way time ' + '$[s]$', fontsize='12') ax3.set_xlabel(r'$kg/m^2s^2$ ', fontsize='16') ax3.set_ylim(0, 3) ax3.set_xticks([0.0e7, 0.5e7, 1.0e7, 1.5e7, 2.0e7]) ax3.invert_yaxis() ax3.grid() ax4 = f2.add_axes([0.8, 0.1, 0.2, 0.9]) ppl.wiggle_plot(ax4, t[:-1], synth, scaling=10, fill='pos') ax4.set_ylim(0, 3) ax4.invert_yaxis() ax4.grid() for top, depth in tops.items(): f2.axes[0].axhline(y=float(depth), color='b', lw=2, alpha=0.5, xmin=0.05, xmax=0.95) f2.axes[0].text(x=1e7, y=float(depth) - 0.015, s=top, alpha=0.75, color='k', fontsize='12', horizontalalignment='center', verticalalignment='center', bbox=dict(facecolor='white', alpha=0.5, lw=0.5), weight='light') for top, depth in tops.items(): f2.axes[1].axhline(y=float(depth), color='b', lw=2, alpha=0.5, xmin=0.05, xmax=0.95) for i in range(2, 4): for twt in tops_twt.values(): f2.axes[i].axhline(y=float(twt), color='b', lw=2, alpha=0.5, xmin=0.05, xmax=0.95)
class PlotTestCase(unittest.TestCase): wp = Project() well_table = { os.path.join(wp.working_dir, 'test_data/Well D.las'): { 'Given well name': 'WELL_D', 'logs': { 'ac': 'Sonic', 'acs': 'Shear sonic', 'cali': 'Caliper', 'den': 'Density', 'neu': 'Neutron density', 'gr': 'Gamma ray', 'rdep': 'Resistivity', 'rmed': 'Resistivity', 'rsha': 'Resistivity', 'neu': 'Neutron density' }, 'Note': 'Some notes for well A' } } wis = { 'WELL_D': { 'SAND C': [1585.0, 1826.0], 'SHALE C': [1585.0, 1826.0], 'SAND D': [1826.0, 1878.0], 'SAND E': [1878.0, 1984.0], 'SAND F': [1984.0, 2158.0], 'SHALE G': [2158.0, 2211.0], 'SAND H': [2211.0, 2365.0] } } w = Well() w.read_well_table(well_table, 0, block_name='Logs') def test_axis_header(self): templ = uio.project_templates(PlotTestCase.wp.project_table) fig, ax = plt.subplots() log_types = ['P velocity', 'Density', 'Caliper', 'Resistivity'] limits = [[templ[x]['min'], templ[x]['max']] for x in log_types] legends = ['test [{}]'.format(templ[x]['unit']) for x in log_types] styles = [{ 'lw': templ[x]['line width'], 'color': templ[x]['line color'], 'ls': templ[x]['line style'] } for x in log_types] header_plot(ax, limits, legends, styles) plt.show() with self.subTest(): self.assertTrue(True) def test_axis_plot(self): templ = uio.project_templates(PlotTestCase.wp.project_table) fig = plt.figure() ax = fig.add_subplot(2, 2, 1) log_types = ['Gamma ray', 'Caliper'] limits = [[templ[x]['min'], templ[x]['max']] for x in log_types] data = [PlotTestCase.w.get_logs_of_type(x)[0].data for x in log_types] y = PlotTestCase.w.block['Logs'].logs['depth'].data styles = [{ 'lw': templ[x]['line width'], 'color': templ[x]['line color'], 'ls': templ[x]['line style'] } for x in log_types] axis_plot(ax, y, data, limits, styles, nxt=2) plt.show() def test_plot_logs(self): from blixt_utils.io.io import invert_well_table w = PlotTestCase.w templates = uio.project_templates(PlotTestCase.wp.project_table) ppl.plot_logs(w, invert_well_table(PlotTestCase.well_table, 'WELL_D', rename=False), PlotTestCase.wis, "SAND E", templates, buffer=50.) #savefig='C:/users/mblixt/PycharmProjects/blixt_rp/results_folder/test.png') #savefig='C:/Users/marten/PycharmProjects/blixt_rp/results_folder/test.png') with self.subTest(): self.assertTrue(True)
class MasksTestCase(unittest.TestCase): wp = Project(name='MyProject', log_to_stdout=True) # Instead of creating the well table directly from the project table, # we can assure the well table to contain the desired well by # writing it explicitly well_table = {os.path.join(wp.working_dir, 'test_data/Well A.las'): {'Given well name': 'WELL_A', 'logs': { 'vp_dry': 'P velocity', 'vp_sg08': 'P velocity', 'vp_so08': 'P velocity', 'vs_dry': 'S velocity', 'vs_sg08': 'S velocity', 'vs_so08': 'S velocity', 'rho_dry': 'Density', 'rho_sg08': 'Density', 'rho_so08': 'Density', 'phie': 'Porosity', 'vcl': 'Volume'}, 'Note': 'Some notes for well A'}} # las_file = os.path.join(wp.working_dir, 'test_data', 'Well A.las') # my_logs = None tops = { 'WELL_A': {'TOP A': 408.0, 'TOP B': 1560.0, 'TOP C': 1585.0, 'TOP D': 1826.0, 'TOP E': 1878.0, 'TOP F': 1984.0, 'BASE F': 2158.0, 'TOP G': 2158.0, 'TOP H': 2211.0, 'BASE H': 2365.0, 'TOP I': 2365.0, 'TOP J': 2452.0} } wis = {'WELL_A': { 'SAND C': [1585.0, 1826.0], 'SHALE C': [1585.0, 1826.0], 'SAND D': [1826.0, 1878.0], 'SAND E': [1878.0, 1984.0], 'SAND F': [1984.0, 2158.0], 'SHALE G': [2158.0, 2211.0], 'SAND H': [2211.0, 2365.0] }} def test_calc_mask(self): lmt = 0.1 w, phie = create_test_data('phie') masked_length = len(phie[phie < lmt]) # # Create the same mask using the create mask function # cutoff = {'phie': ['<', lmt]} w.calc_mask(cutoff, name=def_msk_name, log_type_input=False) msk = w.block[def_lb_name].masks[def_msk_name].data # Test length with self.subTest(): print(masked_length, len(phie[msk])) self.assertEqual(masked_length, len(phie[msk])) # Test value with self.subTest(): print(np.nanmax(phie[msk]), lmt) self.assertLess(np.nanmax(phie[msk]), lmt) del (w.block[def_lb_name].masks[def_msk_name]) # # Create the same mask using the create mask function with log type # print('Testing log type input: Porosity') w.calc_mask({'Porosity': ['<', lmt]}, name=def_msk_name, log_type_input=True) msk = w.block[def_lb_name].masks[def_msk_name].data # Test length with self.subTest(): print(masked_length, len(phie[msk])) self.assertEqual(masked_length, len(phie[msk])) # Test value with self.subTest(): print(np.nanmax(phie[msk]), lmt) self.assertLess(np.nanmax(phie[msk]), lmt) del (w.block[def_lb_name].masks[def_msk_name]) # # Create mask applying tops # print('Test masking with tops input') w.calc_mask({'Porosity': ['<', lmt]}, name=def_msk_name, tops=MasksTestCase.tops, use_tops=['TOP C', 'BASE F'], log_type_input=True) msk = w.block[def_lb_name].masks[def_msk_name].data # Test value with self.subTest(): print(np.nanmax(phie[msk]), lmt) self.assertLess(np.nanmax(phie[msk]), lmt) del (w.block[def_lb_name].masks[def_msk_name]) # # Create mask from empty cutoffs, allow all data # print('Test mask with empty cutoffs') w.calc_mask({}) msk = w.block[def_lb_name].masks[def_msk_name].data with self.subTest(): print(len(phie), len(phie[msk])) self.assertEqual(len(phie), len(phie[msk])) del (w.block[def_lb_name].masks[def_msk_name]) # # Ask for a mask for a log that does not exists # print('Test mask without any valid logs') any_error = False try: w.calc_mask({'Saturation': ['<', lmt]}, name=def_msk_name, log_type_input=True) except: any_error = True # Test if error was raised with self.subTest(): self.assertFalse(any_error) # # Create mask applying working intervals # print('Test masking with working intervals input') w.calc_mask({'Porosity': ['<', lmt]}, name=def_msk_name, wis=MasksTestCase.wis, wi_name='SAND E', log_type_input=True) msk = w.block[def_lb_name].masks[def_msk_name].data # Test value with self.subTest(): print(np.nanmax(phie[msk]), lmt) self.assertLess(np.nanmax(phie[msk]), lmt) del (w.block[def_lb_name].masks[def_msk_name]) # # Create mask using working intervals, but no cutoffs # print('Test masking with working intervals, but no cutoff') w.calc_mask({}, name=def_msk_name, wis=MasksTestCase.wis, wi_name='SAND E') msk = w.block[def_lb_name].masks[def_msk_name].data print('SAND E MD limits in WELL_A:', MasksTestCase.wis['WELL_A']['SAND E']) depths = w.block[def_lb_name].get_md()[msk] print('Masked MD min and max:', depths.min(), depths.max()) # w.calc_mask({'sw': ['<', lmt]}, name=def_msk_name, log_type_input=False) # msk = w.block[def_lb_name].masks[def_msk_name].data ## Test length # with self.subTest(): # print(masked_length, len(phie[msk])) # self.assertEqual(masked_length, len(phie[msk])) def test_apply_mask(self): lmt = 0.1 w, phie = create_test_data('phie') masked_length = len(phie[phie < lmt]) cutoff = {'phie': ['<', lmt]} w.calc_mask(cutoff, name=def_msk_name, log_type_input=False) msk = w.block[def_lb_name].masks[def_msk_name].data w.apply_mask(def_msk_name) m_phie = w.block[def_lb_name].logs['phie'].data # Test length with self.subTest(): print(masked_length, len(m_phie)) self.assertEqual(masked_length, len(m_phie)) # Test value with self.subTest(): print(np.nanmax(m_phie), lmt) self.assertLess(np.nanmax(m_phie), lmt) w.calc_mask({}, name=def_msk_name, wis=MasksTestCase.wis, wi_name='Sand F') msk = w.block[def_lb_name].masks[def_msk_name].data w.apply_mask(def_msk_name) print('Sand F MD limits in WELL_A:', MasksTestCase.wis['WELL_A']['SAND F']) depths = w.block[def_lb_name].get_md() print('Masked MD min and max:', depths.min(), depths.max()) def test_append_mask(self): w, phie = create_test_data('phie') lmt1 = 0.05; lmt2 = 0.1 masked_length = len(phie[(phie > lmt1) & (phie < lmt2)]) # create first mask cutoff_1 = {'phie': ['>', lmt1]} w.calc_mask(cutoff_1, name=def_msk_name, log_type_input=False) msk = w.block[def_lb_name].masks[def_msk_name].data # Test value with self.subTest(): print(np.nanmin(phie[msk]), lmt1) self.assertGreater(np.nanmin(phie[msk]), lmt1) # append second mask cutoff_2 = {'phie': ['<', lmt2]} w.calc_mask(cutoff_2, name=def_msk_name, append='AND', log_type_input=False) msk = w.block[def_lb_name].masks[def_msk_name].data # Test value with self.subTest(): print(np.nanmax(phie[msk]), lmt2) self.assertLess(np.nanmin(phie[msk]), lmt2) # Test length with self.subTest(): print(masked_length, len(phie[msk])) print(w.block[def_lb_name].masks[def_msk_name].header.desc) self.assertEqual(masked_length, len(phie[msk]))
class LasTestCase(unittest.TestCase): wp = Project(name='MyProject', log_to_stdout=True) well_table = uio.project_wells(wp.project_table, wp.working_dir) #las_file = list(well_table.keys())[0] las_file = os.path.join(wp.working_dir, 'test_data/Well A.las') #my_logs = well_table[las_file]['logs'] my_logs = None def test_read_las(self): null_value, gen_keys, well_dict = read_las(LasTestCase.las_file) with self.subTest(): print(len(gen_keys), len(list(well_dict['data'].keys()))) self.assertEqual(len(gen_keys), len(list(well_dict['data'].keys()))) def test_header_types(self): """ All header keys in the well header, and Block header should be of the AttribDict type :return: """ w, data = create_test_data('phie') success = True for key in list(w.header.keys()): if key == 'well': # the well name is having a special treatment:-( continue if not isinstance(w.header[key], AttribDict): print(key, w.header[key]) success = False for lblock in list(w.block.keys()): for key in list(w.block[lblock].header.keys()): if key == 'well': continue if not isinstance(w.block[lblock].header[key], AttribDict): print(key, w.block[lblock].header[key]) success = False self.assertTrue(success) def test_null_value(self): """ When reading in a las file, any 'null values' should be replaced with np.nan :return: """ null_value, gen_keys, well_dict = read_las(LasTestCase.las_file) var_name = gen_keys[1] data = np.array(well_dict['data'][var_name]) # data should not contain explicit 'null_value's, so below null_data should have length zero null_data = data[data == float(null_value)] with self.subTest(): print(var_name, null_value, len(null_data)) self.assertEqual(0, len(null_data)) with self.subTest(): # these test data should contain NaN's, so t below should contain some True elements t = np.isnan(data) print('Data contains NaN:', any(t)) self.assertTrue(any(t)) def test_well_info(self): _las_file = os.path.join(LasTestCase.wp.working_dir, 'test_data/WrongWellInfo.las') null_value, gen_keys, well_dict = read_las(_las_file) for key in list(well_dict['well_info'].keys()): print(well_dict['well_info'][key]['value'], type(well_dict['well_info'][key]['value']))
class RpTestCase(unittest.TestCase): wp = Project() well_table = { os.path.join(wp.working_dir, 'test_data/Well D.las'): { 'Given well name': 'WELL_D', 'logs': { 'ac': 'Sonic', 'acs': 'Shear sonic', 'cali': 'Caliper', 'den': 'Density', 'gr': 'Gamma ray', 'rdep': 'Resistivity', 'rmed': 'Resistivity', 'rsha': 'Resistivity', 'neu': 'Neutron density' }, 'Note': 'Some notes for well A' } } wis = { 'WELL_D': { 'SAND C': [1585.0, 1826.0], 'SHALE C': [1585.0, 1826.0], 'SAND D': [1826.0, 1878.0], 'SAND E': [1878.0, 1984.0], 'SAND F': [1984.0, 2158.0], 'SHALE G': [2158.0, 2211.0], 'SAND H': [2211.0, 2365.0] } } w = Well() w.read_well_table(well_table, 0, block_name='Logs') def test_step(self): i = 5 theta = 10. # degrees x1 = np.linspace(1, 10, 10) x2 = np.linspace(2, 11, 10) x3 = np.linspace(3, 12, 10) d1 = rp.step(x1[i], x1[i + 1]) d2 = rp.step(x1, None, along_wiggle=True) incept1 = rp.intercept(x1[i], x1[i + 1], x3[i], x3[i + 1]) incept2 = rp.intercept(x1, None, x3, None, along_wiggle=True) grad1 = rp.gradient(x1[i], x1[i + 1], x2[i], x2[i + 1], x3[i], x3[i + 1]) grad2 = rp.gradient(x1, None, x2, None, x3, None, along_wiggle=True) func1 = rp.reflectivity(x1[i], x1[i + 1], x2[i], x2[i + 1], x3[i], x3[i + 1]) func2 = rp.reflectivity(x1, None, x2, None, x3, None, along_wiggle=True) with self.subTest(): print('Layer based step at i {}: {}'.format(i, d1)) print('Wiggle based step at i {}: {}'.format(i, d2[i])) print('Layer based intercept at i {}: {}'.format(i, incept1)) print('Wiggle based intercept at i {}: {}'.format(i, incept2[i])) print('Layer based gradient at i {}: {}'.format(i, grad1)) print('Wiggle based gradient at i {}: {}'.format(i, grad2[i])) print('Layer based refl. coeff. at i {} at {} deg.: {}'.format( i, theta, func1(theta))) print('Wiggle based refl. coeff. at i {} at {} deg.: {}'.format( i, theta, func2(theta)[i])) self.assertTrue(True) def test_intercept(self): """ Should test if the intercept calculation returns the same result when using 'along_wiggle' as for single layer :return: """ rho = RpTestCase.w.block['Logs'].logs['den'].data vp = cnvrt(RpTestCase.w.block['Logs'].logs['ac'].data, 'us/ft', 'm/s') incept2 = rp.intercept(vp, None, rho, None, along_wiggle=True) i = np.nanargmax(incept2) incept1 = rp.intercept(vp[i], vp[i + 1], rho[i], rho[i + 1]) incept1_2 = rp.intercept(vp[i + 1], vp[i + 2], rho[i + 1], rho[i + 2]) with self.subTest(): print('Layer based intercept at i {}: {}'.format(i, incept1)) print('Layer based intercept at i {}: {}'.format(i + 1, incept1_2)) print('Wiggle based intercept at i {}: {}'.format( i, incept2[i:i + 2])) self.assertTrue(True) def test_gradient(self): """ Should test if the gradient calculation returns the same result when using 'along_wiggle' as for single layer :return: """ rho = RpTestCase.w.block['Logs'].logs['den'].data vp = cnvrt(RpTestCase.w.block['Logs'].logs['ac'].data, 'us/ft', 'm/s') vs = cnvrt(RpTestCase.w.block['Logs'].logs['acs'].data, 'us/ft', 'm/s') grad2 = rp.gradient(vp, None, vs, None, rho, None, along_wiggle=True) i = 10637 grad1 = rp.gradient(vp[i], vp[i + 1], vs[i], vs[i + 1], rho[i], rho[i + 1]) grad1_2 = rp.gradient(vp[i + 1], vp[i + 2], vs[i + 1], vs[i + 2], rho[i + 1], rho[i + 2]) with self.subTest(): print('Layer based gradient at i {}: {}'.format(i, grad1)) print('Layer based gradient at i {}: {}'.format(i + 1, grad1_2)) print('Wiggle based gradient at i {}: {}'.format( i, grad2[i:i + 2])) self.assertTrue(True) def test_reflectivity(self): """ What happens when the input to the reflectivity is an array? :return: """ i = 10637 theta = 10. # degrees rho = RpTestCase.w.block['Logs'].logs['den'].data vp = cnvrt(RpTestCase.w.block['Logs'].logs['ac'].data, 'us/ft', 'm/s') vs = cnvrt(RpTestCase.w.block['Logs'].logs['acs'].data, 'us/ft', 'm/s') func1 = rp.reflectivity(vp[i], vp[i + 1], vs[i], vs[i + 1], rho[i], rho[i + 1]) func1_2 = rp.reflectivity(vp[i + 1], vp[i + 2], vs[i + 1], vs[i + 2], rho[i + 1], rho[i + 2]) func2 = rp.reflectivity(vp, None, vs, None, rho, None, along_wiggle=True) with self.subTest(): print('Layer based refl. coeff. at i {} at {} deg.: {}'.format( i, theta, func1(theta))) print('Layer based refl. coeff. at i {} at {} deg.: {}'.format( i + 1, theta, func1_2(theta))) print('Wiggle based refl. coeff. at i {} at {} deg.: {}'.format( i, theta, func2(theta)[i:i + 2])) self.assertTrue(True) def test_greenberg_castagna(self): """ Example taken from p. 248 in Rock physics handbook, Mavko et al. 1999 :return: """ vp = 3000. f = [0.6, 0.4] mono_mins = ['sandstone', 'SHALE'] gc_coeffs = { 'sandstone': [0., 0.80416, -0.85588], 'limestone': [-0.05508, 1.01677, -1.03049], 'dolomite': [0., 0.58321, -0.07775], 'shale': [0., 0.76969, -0.86735] } vs = rp.greenberg_castagna(vp, f, mono_mins, gc_coeffs) with self.subTest(): print('Greenberg Castagna Vs estimate: {}'.format(vs.value)) self.assertAlmostEqual(vs.value, 1509.58, places=1)
def test_load_wells(self): wp = Project(name='MyProject', log_to_stdout=True) wells = wp.load_all_wells() for _, well in wells.items(): with self.subTest(): self.assertTrue(well, Well)
def test_create_project(self): wp = Project(name='MyProject', log_to_stdout=True) with self.subTest(): print(type(wp)) self.assertTrue(isinstance(wp, Project))
def test(): from core.well import Project import blixt_utils.io.io as uio from core.minerals import MineralMix wi_name = 'SAND E' plot_type = 'AI-VpVs' ref_val = [2695., 1340., 2.35] # Kvitnos shale fig, ax = plt.subplots() wp = Project() log_table = { 'P velocity': 'vp_dry', 'S velocity': 'vs_dry', 'Density': 'rho_dry', 'Porosity': 'phie', 'Volume': 'vcl' } wis = uio.project_working_intervals(wp.project_table) templates = uio.project_templates(wp.project_table) cutoffs = {'Volume': ['<', 0.4], 'Porosity': ['>', 0.1]} wells = wp.load_all_wells() plot_rp(wells, log_table, wis, wi_name, cutoffs, templates, plot_type=plot_type, ref_val=ref_val, fig=fig, ax=ax) # Calculate average properties of mineral mix in desired working interval mm = MineralMix() mm.read_excel(wp.project_table) _rho_min = np.ones(0) # zero length empty array _k_min = np.ones(0) _mu_min = np.ones(0) for well in wells.values(): # calculate the mask for the given cut-offs, and for the given working interval well.calc_mask(cutoffs, wis=wis, wi_name=wi_name, name='this_mask', log_table=log_table) mask = well.block['Logs'].masks['this_mask'].data _rho_min = np.append( _rho_min, well.calc_vrh_bounds(mm, param='rho', wis=wis, method='Voigt')[wi_name][mask]) _k_min = np.append( _k_min, well.calc_vrh_bounds(mm, param='k', wis=wis, method='Voigt-Reuss-Hill')[wi_name][mask]) _mu_min = np.append( _mu_min, well.calc_vrh_bounds(mm, param='mu', wis=wis, method='Voigt-Reuss-Hill')[wi_name][mask]) rho_min = np.nanmean(_rho_min) k_min = np.nanmean(_k_min) mu_min = np.nanmean(_mu_min) phi = np.linspace(0.05, 0.3, 6) sw = np.array([0.8, 0.95, 1.]) sizes = np.empty((sw.size, phi.size)) colors = np.array([np.ones(6) * 100 * (i + 1)**2 for i in range(3)]) / 900. # iterate over all phi values for i, val in enumerate(phi): sizes[:, i] = 10 + (40 * val)**2 xx, yy = plot_rpt(phi, rpt_phi_sw, sw, { 'plot_type': plot_type, 'ref_value': ref_val, 'model': 'stiff', 'rho_min': rho_min, 'k_min': k_min, 'mu_min': mu_min }, sizes, colors, fig=fig, ax=ax) # Annotate rpt dx = 0 dy = 0.0 plt.text(xx[-1][-1] + dx, yy[-1][-1] + dy, '$\phi={:.02f}$'.format(phi[-1]), **opt1) plt.text(xx[-1][0] + dx, yy[-1][0] + dy, '$\phi={:.02f}$'.format(phi[0]), **opt1) for i, _sw in enumerate(sw): plt.text(xx[i][-1] + dx, yy[i][-1] + dy, '$S_w={:.02f}$'.format(_sw), **opt2) plt.show()