'''
Python module for reservoir interpretation from plots
'''
from __future__ import annotations
import seaborn as sns
import numpy as np
import pandas as pd
from itertools import cycle
from random import choice
from matplotlib import pyplot as plt
from matplotlib.ticker import FormatStrFormatter
[docs]def crossPlot(df:pd.DataFrame, column_x:str, column_y:str, hue:str=None,
color_code:str=None, figsize:tuple=(20,7), rhob_fluid:float=1., res_name:str=None, cmap='viridis'):
r'''
Plots the cross plot relationship of density against porosity on compatible scales
to facilitate in identification of reservoir type and its fluid type.
This code was initially written by Yohanes Nuwara but was modified to give
the resulting plot a more classic and pretty view.
Parameters
----------
df : pd.DataFrame
Dataframe of well
column_x : str
Porosity column
column_y : str
Density column
hue : str
Column to color code the scatter plot by
color_code : str default None
Color code typing. If 'num', arg `hue` must be a continuous column.
If 'cat', argument `hue` must be a categorical column
figsize : tuple
Size of plot
rhob_fluid : float, default 1.0
Fluid density
res_name : str
Reservoir name
cmap : str
color map
Returns
-------
A plot showing the neutron-density cross plot
Example
-------
>>> from petrolib.interp import crossPlot
>>> crossPlot(df=df, column_x='NPHI', column_y='RHOB', res_name='RES_A', color_code='num', hue='GR')
'''
plt.figure(figsize=figsize)
# plt.style.use('classic')
plt.grid(True)
plt.minorticks_on()
plt.grid(which='major', linestyle=':', linewidth='1', color='black')
lsX = np.arange(0, 0.55, 0.05)
ssSnpX = np.empty((np.size(lsX),0), float)
dolSnpX = np.empty((np.size(lsX),0), float)
ssCnlX = np.empty((np.size(lsX),0), float)
dolCnlX = np.empty((np.size(lsX),0), float)
for i in np.nditer(lsX):
ssSnpX = np.append(ssSnpX, np.roots([0.222, 1.021, 0.024 - i])[1])
dolSnpX = np.append(dolSnpX, np.roots([0.6, 0.749, -0.00434 - i])[1])
ssCnlX = np.append(ssCnlX, np.roots([0.222, 1.021, 0.039 - i])[1])
dolCnlX = np.append(dolCnlX, np.roots([1.40, 0.389, -0.01259 - i])[1])
densma_Ls = 2.71; densma_Ss = 2.65; densma_Dol = 2.87 #densma: density matrix
denLs = (rhob_fluid - densma_Ls) * lsX + densma_Ls
denSs = (rhob_fluid - densma_Ss) * lsX + densma_Ss
denDol = (rhob_fluid - densma_Dol) * lsX + densma_Dol
# plot the sand, limestone, and dolomite line
plt.plot(ssCnlX, denSs, '.-', color='blue', markersize=10, label = 'Sandstone',linewidth=2.)
plt.plot(lsX, denLs, '.-', color='green', markersize=10, label = 'Limestone',linewidth=2.)
plt.plot(dolCnlX, denDol, '.-', color='red', markersize=10, label = 'Dolomite',linewidth=2.)
plt.plot([ssCnlX, lsX, dolCnlX], [denSs, denLs, denDol], '--', color='black', linewidth=2.)
#ticks added to the opposite side of the plot
plt.tick_params(bottom=True, top=True,
left=True, right=True, labelbottom=True,
labeltop=True, labelleft=True, labelright=True)
if color_code == 'num':
# plot data with color of the continuous variable defined (depth, vsh, etc.)
plt.scatter(df[column_x], df[column_y], c=df[hue], cmap=cmap)
plt.colorbar(label=hue, orientation='vertical', fraction=.057, pad=0.05)
elif color_code == 'cat':
# plot data with color of each unique value in the catgorical column
sns.scatterplot(data=df, x=column_x, y=column_y, hue=hue, ec=None)
elif color_code == None:
#plot with no coloring
cycol = cycle('bgrcmk')
plt.scatter(df[column_x], df[column_y], c=choice(next(cycol)))
plt.legend(loc='upper left')
plt.title(f'Neutron-Density Cross Plot of RES {res_name}', size=20, pad=20)
plt.xlim(-0.15, .60)
plt.ylim(3, 1.3)
plt.xlabel('$\phi (m3/m3) $',fontsize=18); plt.ylabel(r'$\rho (g/cm3)$', fontsize=18)
plt.show()
[docs]def picketPlot(df:pd.DataFrame, rt:str='RT', por:str='NPHI', rwa:float=0.018,
a:float=1., m:float=1.8, n:float=2., res_name:str=None, figsize:tuple=(20, 8),
hue:str=None, color_code:str=None, cmap=None):
r'''
Plot Pickett plot based on a pattern recognition approach to solving Archie’s equation. The resistivity
and porosity logs are plotted on a logarithmic scales to evaluate formation characteristics of conventional, granular reservoirs.
Read more here: https://wiki.seg.org/wiki/Pickett_plot
Parameters
----------
df : pd.DataFrame
Dataframe
rt : str
Resistivity column
rwa : float default 0.03
Formation water resisitivity
a : float default, 1.
Turtuosity factor
m : float default 2.
Cementation factor
n : float default 2.
Saturation exponent
res_name : str
Reservoir/Zone name
figsize : tuple
Size of plot
hue : str
Column to color code the scatter plot by.
color_code : str default None
Color code typing. If 'num', arg `hue` must be a continuous column
If 'cat', argument `hue` must be a categorical column
if None, there is no color coding
cmap : str
Color map option
Example
-------
>>> import petrolib.interp.picketPlot
>>> picketPlot(df, color_code='num', hue='GR', cmap='rainbow')
>>> from petrolib.interp import picketPlot
>>> picketPlot(df, rt='RT', por='NPHI')
'''
plt.figure(figsize=figsize)
# plt.style.use('classic')
plt.grid(True)
plt.minorticks_on()
plt.grid(which='major', linestyle='-', linewidth='1.5', color='black')
plt.grid(which='minor', linestyle=':', linewidth='1', color='black')
plt.title(f'Pickett Plot of RES {res_name}', size=20, pad=17)
if color_code == None:
assert hue == None and cmap ==None, 'Set hue and cmap to None'
plt.loglog(df[rt], df[por], 'bo')
elif color_code=='num':
#for continuous or numerical column
assert hue != None and cmap !=None, 'Set hue and cmap values'
plt.scatter(df[rt], df[por], c=df[hue], cmap=cmap)
plt.colorbar(label=hue, orientation='vertical', fraction=.057, pad=0.05)
plt.yscale('log'); plt.xscale('log')
elif color_code=='cat':
# for catgorical column
assert hue != None, 'Set hue value'
sns.scatterplot(data=df, x=rt, y=por, hue=hue, ec=None)
plt.yscale('log'); plt.xscale('log')
plt.legend(loc='best')
plt.xlim(0.1,1000)
plt.ylim(0.01,1.)
plt.ylabel('$ \phi Porosity $', fontsize=18); plt.xlabel('$ \Omega m (R_{t}) $', fontsize=18)
plt.gca().xaxis.set_major_formatter(FormatStrFormatter("%.1f"))
plt.gca().yaxis.set_major_formatter(FormatStrFormatter("%.1f"))
#ticks added to the opposite side of the plot
plt.tick_params(bottom=True, top=True,
left=True, right=True, labelbottom=True,
labeltop=True, labelleft=True, labelright=True)
#saturation lines
sw = (.8,0.6,0.4,0.2, 0.1)
phi = (0.01,1)
rt = np.zeros((len(sw), len(phi)))
for i in range (0, len(sw)):
for j in range (0,len(phi)):
rt_result = ((a*rwa)/(sw[i]**n)/(phi[j]**m))
rt[i,j] = rt_result
for i in range(0,len(sw)):
plt.plot(rt[i], phi, label=str(int(sw[i]*100))+'%')
plt.legend(loc='best')
plt.show()