Source code for rockphypy.QI


import matplotlib.colors
cmap_sand = matplotlib.colors.LinearSegmentedColormap.from_list("", ["green","orange","yellow"])
cmap_kde=matplotlib.colors.LinearSegmentedColormap.from_list("", ['white','grey','#4895d9','#65d698',"yellow",'yellow'])
# colormap  
cmap_cem=matplotlib.colors.LinearSegmentedColormap.from_list("", [(0,'blue'),(0.05,'#0a75ad'),(0.05,'#4ca3dd'),(0.1,'#20b2aa'),(0.4,"#008000"),(0.6,"yellow"),(1,"red")])

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from kde_diffusion import kde2d

# import matplotlib.pyplot as plt


from rockphypy.utils import utils
from rockphypy.EM import EM
from rockphypy.GM import GM
from rockphypy.Emp import Empirical
from rockphypy.Fluid import Fluid


[docs]class QI: """ Useful functionalities for quantitative intepretation and relevant tasks. """ def __init__(self,Vp,phi,**kwargs): #phi,den,Vsh,eff_stress, TVD, """initialize the parameters for various QI plots. e.g., phi,den,Vsh,eff_stress, TVD can be given depending on required the input parameters to the plot funtion. Parameters ---------- Vp : array p wave velocity phi : array porosity """ self.Vp=Vp self.phi=phi self.Vsh=kwargs.get('Vsh') self.Vs=kwargs.get('Vs') self.den=kwargs.get('den') self.eff_stress= kwargs.get('eff_stress') self.TVD=kwargs.get('TVD') pass
[docs] @staticmethod def matrix_modulus(vsh,phi_c,phi_0,M_sh,M_g, M_c): """Calculate the modulus of rock matrix as a function of porosity variation caused by cementation, Note that the rock matrix contains everything excluding pore space. Parameters ---------- vsh : float bulk volume fraction of shale, can be derived from well log. phi_c : float critical porosity phi_0 : float or array static porosity during cementation ranging between 0.4 to 0 should be phi when phi is an array of porosity M_sh : float modulus of shale M_g : float modulus of grain material M_c : float modulus of cement Returns ------- float or array M_mat: updated matrix modulus """ #phi_0=1-np.linspace(0.0,phi_c,100) # porosity state from the begining of cementation to the end of cementation fc = phi_c - phi_0 # Fraction of cement in the rock vsh_s=vsh/(1-phi_0)# shale volume fraction in matrix fgs = (1-phi_c-vsh)/(1-phi_0) #faction of grain in the matrix fcs = fc/(1-phi_0) # fraction of cement in the matrix if vsh==0: volume=np.vstack((fgs,fcs)).T M=np.array([M_g,M_c]) _,_,M_mat=EM.VRH(volume, M) return M_mat else: volume=np.vstack((vsh_s,fgs,fcs)).T M=np.array([M_sh, M_g,M_c]) _,_,M_mat=EM.VRH(volume, M) return M_mat ## it is a numpy ndarray
[docs] @staticmethod def den_matrix(vsh,phi_c,phi_0,den_sh,den_g, den_c): """Calculate the matrix density as a function of porosity variation caused by cementation. Parameters ---------- vsh : float bulk volume fraction of shale, can be derived from well log. phi_c : float critical porosity phi_0 : float or array static porosity during cementation ranging between 0.4 to 0 should be phi when phi is an array of porosity den_sh : float density of the clay den_g : float density of the grain den_c : float density of the cement Returns ------- float or array den_mat: updated matrix density """ fc = phi_c - phi_0 # Fraction of cement in the rock vsh_s=vsh/(1-phi_0)# shale volume fraction in matrix fgs = (1-phi_c-vsh)/(1-phi_0) #faction of grain in the matrix fcs = fc/(1-phi_0) # fraction of cement in the matrix volume=np.vstack((vsh_s,fgs,fcs)).T D=np.array([den_sh,den_g, den_c]) den_mat,_,_=EM.VRH(volume, D) return den_mat
[docs] @staticmethod def screening(Dqz,Kqz,Gqz,Dsh,Ksh,Gsh,Dc,Kc,Gc,Db,Kb,phib_p,phi_c,sigma,vsh,scheme,f, Cn): """compute elastic bounds used for rock physics screening, the lower bound is computed using friable sand model, and the upper bound is contact cement model blend with increasing cement model. Parameters ---------- K0 : float Bulk modulus of grain material in GPa G0 : float Shear modulus of grain material in GPa Dqz : float Density of the grain. not limited to quartz grain Kqz : float Bulk modulus of grain material in GPa Gqz : float Shear modulus of grain material in GPa Dsh : float density the clay Ksh : float bulk modulus of the clay Gsh : float shear modulus of the clay Dc : float density of the cement Kc : float Bulk modulus of cement Gc : float Shear modulus of cement Db : float density of the pore fluid Kb : float bulk modulus of the pore fluid phib_p : float adjusted high porosity end memeber phic : float Critical Porosity sigma : float or array-like effective stress vsh : _type_ _description_ scheme : int Scheme of cement deposition 1=cement deposited at grain contacts 2=cement deposited at grain surfaces f : float reduced shear factor between 0 and 1 0=dry pack with inifinitely rough spheres; 1=dry pack with infinitely smooth spheres Cn : float coordination number Returns ------- array phi,vp1,vp2,vp3,vs1,vs2,vs3: porosity and velocities required for elastic diagnostics bounds """ #------------calculate the effective bulk and shear modulus------------# _,_,K0=EM.VRH(np.array([vsh,1-vsh]),np.array([Ksh,Kqz])) _,_,G0=EM.VRH(np.array([vsh,1-vsh]),np.array([Gsh,Gqz])) # this is only two component sandstone, when sandstone is multimineralogy, vectorization should apply here D0=Dsh*vsh+Dqz*(1-vsh) phi = np.linspace(1e-7,phi_c,100) #define porosity range according to critical porosity Kdry1, Gdry1 = GM.softsand(K0, G0, phi, phi_c, Cn, sigma,f) # frible sand Kdry3, Gdry3= GM.contactcement(K0, G0, Kc, Gc, phi, phi_c, Cn, scheme) # contact cement K_mat=QI.matrix_modulus(vsh,phi_c,phi,Ksh,Kqz,Kc) # update the mineral matrix modulus for contact cement model Den_mat=QI.den_matrix(vsh,phi_c,phi,Dsh,Dqz,Dc) # update matrix density for contact cement model Ksat1, Gsat1 = Fluid.Gassmann(Kdry1,Gdry1,K0,Kb,phi) Ksat3, Gsat3 = Fluid.Gassmann(Kdry3,Gdry3,K_mat,Kb,phi) rho = D0*(1-phi)+Db*phi # update bulk density for saturated soft sand model and stiff sand rho_cst=Den_mat*(1-phi)+Db*phi # update bulk density for saturated contact cement model vp1,vs1= utils.V(Ksat1,Gsat1,rho) vp3,vs3= utils.V(Ksat3,Gsat3,rho_cst) vp3[phi<phib_p]=np.nan # contact cement model is invalid for small porosity, here we set the critical cement limit is 10$ vs3[phi<phib_p]=np.nan Kdry2, Gdry2 = GM.MUHS(K0, G0, Kc,Gc,phi, phib_p,phi_c, Cn,scheme) #increasing cement model Ksat2, Gsat2 = Fluid.Gassmann(Kdry2,Gdry2,K0,Kb,phi) vp2,vs2= utils.V(Ksat2,Gsat2,rho) vp2[phi>phib_p]=np.nan # increasing cement model is invalid for small porosity vs2[phi>phib_p]=np.nan return phi,vp1,vp2,vp3,vs1,vs2,vs3
[docs] def screening_plot(self,phi,vp1,vp2,vp3,cmap=cmap_sand): """plot the rock physics screening crossplot Parameters ---------- phi : array porosity vp1 : array lower bound velocities modeled using friable sandstone model by default . vp2 : array upper bound velocities modeled using MUSH model by default. vp3 : array upper bound velocities modeled using contact cement model by default. cmap : string, optional colormap, can be default colormaps in matplotlib, by default using the customized colormap: cmap_sand Returns ------- object elastic bounds plot used for rock physics screening """ fig,ax=plt.subplots() fig.set_size_inches(7, 6) ax.plot(phi,vp3,'-k', lw=4, alpha=0.7) ax.plot(phi,vp1,'--k', lw=2, alpha=0.7) ax.plot(phi,vp2,'-k',lw=4, alpha=0.7) ax.set_ylabel('Vp') ax.set_xlabel('Porosity') ax.grid(ls='--',alpha=0.7) plt.scatter(self.phi,self.Vp,c=1-self.Vsh,vmin=0, vmax=1,edgecolors='grey',s=100,alpha=1,cmap=cmap) cbar=plt.colorbar() cbar.set_label(r'$\rm V_{Sand}$') return fig
[docs] @staticmethod def normalize(density): ''' normalize the kde with respect to the maximum value and map value to 0-1 ''' den_max=density.max() # map value new=1-(den_max-density)/den_max return new
[docs] def kde_plot(self,phi,v1,v2,v3,cmap=cmap_kde,vels='Vp',n=64): """plot field data or measurements as 2D probability density functions in the elastic bounds cross plot Parameters ---------- phi : array porosity vp1 : array lower bound velocities modeled using friable sandstone model by default . vp2 : array upper bound velocities modeled using MUSH model by default. vp3 : array upper bound velocities modeled using contact cement model by default. cmap : string, optional colormap, can be default colormaps in matplotlib, by default using the customized colormap: cmap_kde vels : str, optional choose either P wave or S wave velocity for plotting, by default 'Vp' n : int, optional grid parameter used in KDE-diffusion, by default 64 Returns ------- object KDE plot with elastic bounds """ #KDE via diffusion on data fig,ax=plt.subplots() fig.set_size_inches(7, 6) ax.plot(phi,v3,'-k', lw=4, alpha=0.7) ax.plot(phi,v1,'--k', lw=2, alpha=0.7) ax.plot(phi,v2,'-k',lw=4, alpha=0.7) #ax.set_ylabel('Vp') ax.set_xlabel('Porosity') ax.set_xlim([-0.007,0.407]) if vels=='Vp': (density, grid, bandwidth) = kde2d(self.phi, self.Vp, n=n, limits=None) # map density to 0 and 1 density=QI.normalize(density) # extremely low density is mapped to 0 density[density<0.0001]=0 plt.pcolormesh(grid[0],grid[1], density.T, cmap=cmap_kde,shading='auto') cbar=plt.colorbar() cbar.set_label('KDE Normalized') plt.contour(grid[0],grid[1], density.T,colors='white',linewidths=0.5) ax.set_ylabel('Vp') elif vels=='Vs': (density, grid, bandwidth) = kde2d(self.phi, self.Vs, n=n, limits=None) # map density to 0 and 1 density=QI.normalize(density) # extremely low density is mapped to 0 density[density<0.0001]=0 plt.pcolormesh(grid[0],grid[1], density.T, cmap=cmap_kde,shading='auto') cbar=plt.colorbar() cbar.set_label('KDE Normalized') plt.contour(grid[0],grid[1], density.T,colors='white',linewidths=0.5) ax.set_ylabel('Vs') ax.grid(alpha=1) return fig
[docs] @staticmethod def cst_vels(phi_b,K0,D0,G0,phi,phi_c,Cn,Kc,Gc,Db,Kb,scheme,vsh,Dsh,Dqz,Dc): """compute velocities using constant cement model at different cement amounts Parameters ---------- phi_b : float adjusted porosity for constant cemnet model K0 : float Bulk modulus of grain material in GPa D0 : float Density of grain material G0 : float Shear modulus of grain material in GPa phi : float porosity phi_c : float Critical Porosity Cn : float critical porosity Kc : float Bulk modulus of cement Gc : float Shear modulus of cement Db : float density of the pore fluid Kb : float bulk modulus of the pore fluid scheme : int Scheme of cement deposition 1=cement deposited at grain contacts 2=cement deposited at grain surfaces vsh : float shale content Dsh : float density the clay Dqz : float Density of the grain. not limited to quartz grain Dc : float density of the cement Returns ------- array vp,vs: velocities given by constant cement model """ Kdry, Gdry=GM.constantcement(phi_b, K0, G0,Kc, Gc, phi, phi_c, Cn,scheme ) D=QI.den_matrix(vsh,phi_c,phi_b,Dsh,Dqz,Dc) vp,vs,_= Fluid.vels(Kdry,Gdry,K0,D,Kb,Db,phi) vp[phi>phi_b]=np.nan vs[phi>phi_b]=np.nan return vp,vs
[docs] @staticmethod def cst_plot(Dqz,Kqz,Gqz,Dsh,Ksh,Gsh,Dc,Kc,Gc,Db,Kb,phib,phib_p,phi_c,sigma,vsh,Cn, scheme,f): """Diagnostic plot with constant cement model lines Parameters ---------- Dqz : float Density of the grain. not limited to quartz grain Kqz : float Bulk modulus of grain material in GPa Gqz : float Shear modulus of grain material in GPa Dsh : float density the clay Ksh : float bulk modulus of the clay Gsh : float shear modulus of the clay Dc : float density of the cement Kc : float Bulk modulus of cement Gc : float Shear modulus of cement Db : float density of the pore fluid Kb : float bulk modulus of the pore fluid phib : float adjusted high porosity end memeber for constant cement model phib_p : array or list posoities used to drawing the constant cement lines phi_c : float Critical Porosity sigma : float effective stress vsh : float shale content Cn : float coordination number scheme : int Scheme of cement deposition 1=cement deposited at grain contacts 2=cement deposited at grain surfaces f : float reduced shear factor between 0 and 1 0=dry pack with inifinitely rough spheres; 1=dry pack with infinitely smooth spheres Returns ------- object fig,ax: constant cement diagnostic plot """ _,_,K0=EM.VRH(np.array([vsh,1-vsh]),np.array([Ksh,Kqz])) _,_,G0=EM.VRH(np.array([vsh,1-vsh]),np.array([Gsh,Gqz])) # this is only two component sandstone, when sandstone is multimineralogy, vectorization should apply here D0=Dsh*vsh+Dqz*(1-vsh) phi,vp1,vp2,vp3,vs1,vs2,vs3 = QI.screening(Dqz,Kqz,Gqz,Dsh,Ksh,Gsh,Dc,Kc,Gc,Db,Kb,phib,phi_c,sigma,vsh,scheme,f, Cn) fig,ax=plt.subplots() ax.plot(phi,vp3,'-k', lw=4, alpha=0.7) ax.plot(phi,vp1,'-k', lw=3, alpha=0.7) for i in phib_p: ax.plot(phi,QI.cst_vels(i,K0,D0,G0,phi,phi_c,Cn,Kc,Gc,Db,Kb,scheme,vsh,Dsh,Dqz,Dc)[0],'--k',lw=2, alpha=0.7) ax.set_ylabel('Vp (m/s)') ax.set_xlabel('porosity') ax.grid() #plt.legend() return fig,ax
[docs] @staticmethod def cal_v_const(Dqz,Kqz,Gqz,Dsh,Ksh,Gsh,Dc,Kc,Gc,Db,Kb,phi_b,phi_c,vsh,phi,scheme): """input real data porosity and caculate the theoretical constant cement model velocity value. Note: input porosity cannot be zero, otherwise the returned velocities are Nan. Parameters ---------- Dqz : float Density of the grain. not limited to quartz grain Kqz : float Bulk modulus of grain material in GPa Gqz : float Shear modulus of grain material in GPa Dsh : float density the clay Ksh : float bulk modulus of the clay Gsh : float shear modulus of the clay Dc : float density of the cement Kc : float Bulk modulus of cement Gc : float Shear modulus of cement Db : float density of the pore fluid Kb : float bulk modulus of the pore fluid phi_b : _type_ _description_ phi_c : float Critical Porosity vsh : float shale content phi : array porosity scheme : int Scheme of cement deposition 1=cement deposited at grain contacts 2=cement deposited at grain surfaces Returns ------- array vp,vs: constant cement velocities """ _,_,K0=EM.VRH(np.array([vsh,1-vsh]),np.array([Ksh,Kqz])) _,_,G0=EM.VRH(np.array([vsh,1-vsh]),np.array([Gsh,Gqz])) # this is only two component sandstone, when sandstone is multimineralogy, vectorization should apply here D0=Dsh*vsh+Dqz*(1-vsh) Cn=Empirical.Cp(phi_c) ## calculate coordination number Kdry, Gdry=GM.constantcement(phi_b, K0, G0, Kc, Gc, phi, phi_c, Cn, scheme ) vp,vs,_= Fluid.vels(Kdry,Gdry,K0,D0,Kb,Db,phi) return vp,vs
[docs] def estimate_cem(self,vcem_seeds,Kqz,Gqz,Ksh,Gsh,phi_c,Cn,Kc,Gc,Db,Kb,scheme,vsh,Dsh,Dqz,Dc): """Estimate cement amount for well log data using constant cement model crossplot. Parameters ---------- vcem_seeds : array or list some predefined values at which constant cement lines are calculated Kqz : float Bulk modulus of grain material in GPa Gqz : float Shear modulus of grain material in GPa Ksh : float bulk modulus of the clay Gsh : float shear modulus of the clay phi_c : float Critical Porosity Cn : float coordination number Kc : float Bulk modulus of cement Gc : float Shear modulus of cement Db : float density of the pore fluid Kb : float bulk modulus of the pore fluid scheme : int Scheme of cement deposition 1=cement deposited at grain contacts 2=cement deposited at grain surfaces vsh : float shale content Dsh : float density the clay Dqz : float Density of the grain. not limited to quartz grain Dc : float density of the cement Returns ------- array cement amount estimation for each well log data points """ _,_,K0=EM.VRH(np.array([vsh,1-vsh]),np.array([Ksh,Kqz])) _,_,G0=EM.VRH(np.array([vsh,1-vsh]),np.array([Gsh,Gqz])) # this is only two component sandstone, when sandstone is multimineralogy, vectorization should apply here D0=Dsh*vsh+Dqz*(1-vsh) # create an empty dataframe df = pd.DataFrame() # corresponding porosity is phi_c-vcem_seeds[i] for i in vcem_seeds: VP,VS=QI.cal_v_const(Dqz,Kqz,Gqz,Dsh,Ksh,Gsh,Dc,Kc,Gc,Db,Kb,phi_c-i,phi_c,vsh,self.phi,scheme) df['vcem_vp'+str(i)]=VP # decide if Vcem is 0 or not, otherwise, interpolate between two closet velocities df['vcem']=np.nan for i, val in enumerate(self.Vp): array= np.array(df.iloc[i, :-1]) if val <= array[0]: df.vcem.values[i]=0 #df.iloc[i]['vcem']=0 elif val >=array[-1]: df.vcem.values[i]=0.1 #df.iloc[i]['vcem']=0.1 else: big = array[array<val].max()# the largest element of myArr less than myNumber small = array[array>val].min()# the smallest element of myArr greater than myNumber #df.vcem.values[i] df.vcem.values[i]=(val-small)/(big-small)*(vcem_seeds[array==big]-vcem_seeds[array==small])+vcem_seeds[array==small] return df.vcem.values
[docs] def cement_diag_plot(self,vcem,Dqz,Kqz,Gqz,Dsh,Ksh,Gsh,Dc,Kc,Gc,Db,Kb,phib,phib_p,phi_c,sigma,vsh,Cn, scheme,f): """_summary_ Parameters ---------- vcem : _type_ _description_ Dqz : float Density of the grain. not limited to quartz grain Kqz : float Bulk modulus of grain material in GPa Gqz : float Shear modulus of grain material in GPa Dsh : float density the clay Ksh : float bulk modulus of the clay Gsh : float shear modulus of the clay Dc : float density of the cement Kc : float Bulk modulus of cement Gc : float Shear modulus of cement Db : float density of the pore fluid Kb : float bulk modulus of the pore fluid phib : float adjusted high porosity end memeber for constant cement model phib_p : array or list posoities used to drawing the constant cement lines phi_c : float Critical Porosity sigma : float or array-like effective stress vsh : float shale content Cn : float coordination number scheme : int Scheme of cement deposition 1=cement deposited at grain contacts 2=cement deposited at grain surfaces f : float reduced shear factor between 0 and 1 0=dry pack with inifinitely rough spheres; 1=dry pack with infinitely smooth spheres Returns ------- object cross plot for cement estimation """ fig,ax=QI.cst_plot(Dqz,Kqz,Gqz,Dsh,Ksh,Gsh,Dc,Kc,Gc,Db,Kb,phib,phib_p,phi_c,sigma,vsh,Cn, scheme,f) fig.set_size_inches(7, 6) plt.scatter(self.phi,self.Vp,c=vcem,edgecolors='grey',s=100,vmin=0, vmax=0.1,alpha=0.7,cmap=cmap_cem) cbar=plt.colorbar() cbar.set_label(r'$\rm V_{CEM}$') plt.xlabel('Porosity') plt.ylim([1500,6500]) plt.xlim([-0.007,0.507]) return fig
# RPT plot
[docs] @staticmethod def plot_rpt(Kdry,Gdry,K0,D0,Kb,Db,Khc,Dhc,phi,sw): """Create RPT plot given computed Impedance and Vp/Vs ratio. Parameters ---------- Kdry : float or array effective bulk modulus given by rock physics model Gdry : float or array effective shear modulus given by rock physics model K0 : float bulk modulus of grain D0 : float density of grain Kb : float bulk moduluf of brine Db : float density of brine Khc : float bulk modulus of HC Dhc : float density of HC phi : float or array porosity sw : float or array water saturation Returns ------- python onject: fig rpt plot """ # setup empty arrays to store Ip and Vp/Vs values IP=np.empty((phi.size,sw.size)) PS=np.empty((phi.size,sw.size)) ## loop over Sw, computes elastic moduli of fluid mixture and saturated rock properties with Gassmann's equation for i,val in enumerate(sw): Kf=(val/Kb+(1-val)/Khc)**-1 #Kf_mix(val,Kb,Khc) Df = val*Db+(1-val)*Dhc vp,vs,rho= Fluid.vels(Kdry,Gdry,K0,D0,Kf,Df,phi) IP[:,i]=vp*rho PS[:,i]=vp/vs # plot fig=plt.figure(figsize=(10,8)) plt.plot(IP.T, PS.T, '-ok', mec='k', ms=10, mfc='yellow') plt.plot(IP[:,-1], PS[:,-1], '-ok', mec='k', ms=10, mfc='blue')# Brine line plt.xlabel('Acoustic Impedance'), plt.ylabel('Vp/Vs') # for i,val in enumerate(phi): # plt.text(IP[i,-1],PS[i,-1]+.03,'{:.02f}'.format(val), alpha=1,backgroundcolor='0.9') # for i,val in enumerate(sw): # plt.text(IP[-1,i]-100,PS[-1,i],'Gas={:.02f}'.format(1-sw[i]),ha='right',alpha=1) return fig