Python
Python
Example Book
V3.0
This document is part of the “Python for Oil and Gas Industry” course
offered by PEA. It serves as a fulfillment of the required course
materials and is intended for educational purposes.
Example Guide
Copyright(C)
Materials used in connection with this course may be subject to
copyright protection. Materials may include, but are not limited to:
documents, slides, images, audio, and video. Materials in this course
Web site are only for the use of students enrolled in this course, for
purposes associated with this course, and may not be retained for
longer than the class term. Unauthorized retention, duplication,
distribution, or modification of copyrighted materials is strictly
prohibited by law
This Type of plot is used to distinguish the water production source in oil producing wells.
Refer to paper (SPE-30775) for more information about this methodology.
Required Data:
• Water Oil Ratio. Vs Date, Days or Months
What is Plotted:
• Water Oil Ratio.
• Water Oil Ratio Derivative.
Plot Example
Python Code
import math
#Import Data
data = pd.read_csv(r'wor.txt',sep='\t')
#Create Wor Derivative Column and assign it to zero value
data["Wor_"] = [0] * len(data)
#calculate derivative
for i in range(1, len(data)-1):
data["Wor_"][i] = abs((data['Wor'][i+1] - data['Wor'][i-1])/(data['Month'][i+1] -
data['Month'][i-1]))
#Plot Historical Wor
plt.scatter(data['Month'],data['Wor'],label='WOR')
plt.yscale('symlog')
plt.xscale('symlog')
plt.grid()
polyfunc = np.poly1d(np.polyfit(data['Month'],data['Wor'],deg=6))
plt.plot(data.Month,polyfunc(data.Month),label='WOR Fitting')
#Plot Derivative
plt.scatter(data['Month'],data['Wor_'],label='WOR Derivative')
polyfunc = np.poly1d(np.polyfit(data['Month'],data['Wor_'],deg=6))
plt.plot(data.Month,polyfunc(data.Month),label='Wor Derviative Fitting')
#Finishup the plot Title
plt.title("Chan Plot")#Set Title
plt.legend()#Show Legend
Python Code
from matplotlib import pyplot as plt
import pandas as pd
plt.figure(figsize=(16,9))
import numpy as np
import math
#Import Data
data = pd.read_csv(r'inj.txt',sep='\t')
print(data.head())
#Read Reservoir Pressure
p_res = 1080 #psig
#Create New column
data['ii'] = 1/(data['Winj_bbl']/(data['InjP_psig'] - p_res))
#Get Unique Well Names
well_names = data['Well'].unique()
print(well_names)
for well in well_names:
df = data[data['Well'] == well]
plt.plot(range(0,len(df)), df['ii'],label=well)
plt.legend()
plt.grid(True)
plt.xlabel('Months')
plt.ylabel('Injectivity Index')
plt.title("Reverse Injectivity Index")
Knowing how to create maps (generally speaking) is an essential skill for petroleum
engineers. The process of creating maps was somewhat tedious, however python makes it
easier and cheaper to produce maps of any desired variable, without using commercial
reservoir management packages.
Required Data:
• X Coordinate.
• Y Coordinate.
• Any Other variables (Depth, Production, Sw, etc.)
Plot Example
Python Code
import matplotlib.pyplot as plt
plt.style.use('ggplot')
#Set figure size
fig = plt.figure(figsize=(16,8))
import pandas as pd
#import file
df = pd.read_csv('demoxy.txt', sep='\t')
#Get X, Y As List
x= df['XCoordinate']
y= df['YCoordinate']
Z = df['TotalDepth']
df = df.replace({'NaN': 0})
#Plot Triangular Color Filled Contour
plt.tricontourf(x,y,Z,cmap='rainbow')
plt.colorbar()
plt.tricontour(x,y,Z)
#Set Well Shapes
plt.scatter(x,y,color='black',marker='^')
plt.xlabel("X Coordinate")#Plot labels
plt.ylabel("Y Coordinate")#Plot labels
This example illustrates how to plot 3D Oil water contact map, as values are populated from
drilled wells. Knowing OWC is necessary to know if a well is penetrating the water leg thus
derive conclusion for various reservoir management/workover activities.
Required Data:
• X Coordinate.
• Y Coordinate.
• OWC Depth
Plot Example
Python Code
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib qt #View Plot Outside (separate window)
plt.style.use('ggplot')
#Set figure size
fig = plt.figure(figsize=(16,8))
data = pd.read_csv('resdata.txt',sep='\t')
#prepair an empty 3d figure
surf_plot = fig.add_subplot(projection='3d')
#Prep the data
x= data['Xcoord']
y= data['Ycoord']
z= data['OWC']
#plot 3d surface
surf_plot.plot_trisurf(x,y,z,cmap='rainbow',linewidth=0.01,label='Water Table')
#Set depth limits
surf_plot.set_zlim([1600,2000])
surf_plot.invert_zaxis()
This example uses an empirical calculation proposed by Guthrie et. Al(ref API-1955 study),
although no proved empirical methodology exist in with high accuracy, this method is to
approximate the primary recovery factor
Ro=0.114+0.272log(k)+0.256(Sw)−0.136log(μo)−1.538(∅)−0.00035(h)
Required Data:
• Water saturation.
• Oil Viscosity.(cp)
• Permeability(md)
• Payzone thickness
• Porosity
Plot Example
#add figures
grid.add_trace( go.Scatter(x=sat_list,y=rf_list,name='Recovery
Factor',mode='lines+markers') ,row=1,col=1)
grid.add_trace( go.Scatter(x=sat_list,y=prod_list,name='Cum Oil Produced
[STBPD]',mode='lines+markers'),row=2,col=1)
#update plots
grid.update_layout(template='seaborn',title='Ultimate Recovery Empirical Calculations')
grid.update_layout(xaxis_title='Water Saturation')
#show plot
plotly.offline.plot(grid)
In this exercise, we will try to calculate porosity by utilizing density log(using las file), basic
assumptions are made about the matrix density and fluid property. This example is intended
to illustrate how to construct well log tracks using python and a set of 3rd party libraries.
Required Data:
• Las file
• Matrix Density
• In-pore fluid density
Plot Example
In this exercise, we will create and visualize IOIP function using Gradio library. Function
visualizer are very important aspect of python where it takes the code and convert it into
GUI components that are easy to use.
Required Data:
• IOIP Equation
• Gradio Library
• Jupyter Notebook
Plot Example
Python Code
#Import Gradio External Library
import gradio as gr
# Let's define the function:
def ooip(area, h, por, Sw, Boi):
#write the equation:
calc = 7758 * area * por * (1-Sw) /Boi
return calc
#Define all the require Components
#Other Component types can be found @ Gradio.Doc
area = gr.Number(value=150, label="Area")
h = gr.Number(value=100, label="h [ft]")
poro = gr.Slider(minimum=0,maximum=0.9,value=0.1, label="Porosity")
sw = gr.Slider(minimum=0,maximum=0.9,value=0.1, label="Water Sat.")
boi = gr.Number(value=1.02, label="Boi")
# Now let's creat the visuals
view = gr.Interface(fn=ooip, inputs=[area,h, poro, sw, boi], outputs="text")
#View the visuals
view.launch()
Plot Sample:
return fig
#make a dropdown for the date
date_selector = gr.Dropdown(choices=dates, label="Please Select a Well",
value=dates[0])
#Create the main Graphical User Interface
gui = gr.Interface(HI, inputs=[date_selector],
outputs=gr.Plot(), live=True)#make it Live=True to instantly
update when UI changes
gui.launch(inline=True, debug=True, share=False)
Plot:
Code:
import pandas as pd
import matplotlib.pyplot as plt
#Import the data
df = pd.read_csv("production_history.txt", sep="\t")
# get the selected wells:
wells = ["HOGL3_0132", "HOGL3_0432","HOGL_0532","HOGL3_0732","HOGL_0832" ]
#create a new dataframe based on well selection
selected_wells_df = df[ df["UID"].isin(wells) ]
#convert the date column from object to date
df.Date = pd.to_datetime(df.Date)
selected_wells_df.Date = pd.to_datetime(df.Date)
# sort, ascendengly
selected_wells_df.sort_values(by="Date", ascending=True)
# group by dates, since a single date might contain production from various wells
selected_wells_df = selected_wells_df.groupby("Date").sum()
selected_wells_df["Cum-OIL"] = selected_wells_df.OIL.cumsum()
fig, axs = plt.subplots(1,1,figsize=(11,4))
plt.style.use("seaborn-deep")
for i in wells:
df_well = df[ df.UID == i ]
axs.step(df_well.Date, df_well["OIL"], label=i)
ax2 = axs.twinx()
ax2.plot(selected_wells_df.index, selected_wells_df["Cum-OIL"],color="magenta", label="Cum
Prod")
axs.set_ylabel("Production By Well")
ax2.set_ylabel("Cumulative Production")
axs.legend();ax2.legend();ax2.grid(False)
Required Data:
• Wellhead Pressure(psig)
• Gas Flowrate (psig)
• Tubing ID = 6.36 inches(Annulus Production, No Tubing)
What is Plotted:
• Critical Velocity. (From turner equation)
• Actual Gas Velocity (from Qg)
• Unloading Efficiency (Actual Velocity / Critical)
P is WHP in psig
Plot Example
Python Code
from matplotlib import pyplot as plt
import pandas as pd
plt.figure(figsize=(16,9))
import math
#load data
data = pd.read_csv('gasflow.txt',sep='\t')
#Calculate Tubing Area
tubing_id = 6.36/2 #internal radius
tbg_area = math.pow(tubing_id,2)*3.14 / 144 #144 is the convesion from in to ft
#calculate turner velocity
data['Turner'] = 5.62*(67 - 0.0031*data['Whp_psig'])**0.25 /
((0.0031*data['Whp_psig']**0.5))
data['Turner'] = tbg_area * data['Turner'] * 86400/1000000 # Convert Velocity to
Qg_critical
#Plot data
plt.plot(data['Days'],data['Turner'], label="Critical Velocity")
plt.plot(data['Days'],data['Qgas_mmscf'], label="Gas Rate")
plt.legend()
plt.grid(True)
plt.xlabel('Days')
plt.ylabel('Gas Rate - mmscf/day')
plt.title("Liquid Loading Analysis")
#Add Limit to Xaxis(optional)
#plt.xlim(1000,1500)
Back allocation is an industry wide practice used to devide production from a given
separator or surface processing facility to it’s connected wells, this will provide daily
production by multiplying well test values to a calculation allocation factor(something similar
to tuning factor).
Required Data:
• Separator Production (Qo,Qg,Qw)
• Well Test for each well
• Allocation Factor (Calculated)
Plot Example
Libraries used:
• Plotly.
Figure Types:
• Sankey
Python Code
This example illustrates the usage of IPR where multiple well test points are required.
Required Data:
• Well Test Pwf, Q
• Reservoir Pressure
Fetkovitch’s Equation:
𝑄𝑜 = 𝐶 ∗ (𝑃𝑟 2 − 𝑃𝑤𝑓 2 )𝑛
where n = 1/slope when fitted in log-log plot
where C can be calculated once n Is known.
Libraries Used :
• Matplotlib
Plot Example
Python Code:
https://www.peassociations.com/ peassociations.com +91 6205464268
Python For Oil & Gas
import matplotlib.pyplot as plt1
import matplotlib.pyplot as plt
import pandas as pd
import math
import numpy as np
#populate well test data
q_list = [66,134,137,93,321,341]
pwf_list = [1242,1142,1123,1178,719,638]
p_res = 1345 #psig
df_init = {"q_stb":q_list,"pwf_psig":pwf_list}
df_well_test = pd.DataFrame(df_init)
df_well_test['delta^2'] = p_res**2 - df_well_test['pwf_psig']**2
df_well_test['delta_log'] =np.log10(df_well_test['delta^2'])
df_well_test['q_log'] = np.log10(df_well_test['q_stb'])
#plt.scatter(x=df_well_test['q_log'],y=df_well_test['delta_log'])
poly_func = np.poly1d(np.polyfit(df_well_test['q_log'], df_well_test['delta_log'],
deg=1))
pwf_pred = poly_func(df_well_test['q_log'])
#plt.plot(df_well_test['q_log'],list(pwf_pred))
#plt.xscale('linear'),plt.yscale('linear')
n = 1/poly_func.coefficients[0]
c = q_list[0]/(p_res**2 - pwf_list[0]**2)**n
#Construct IPR Data
pwf_range = range(0,p_res,50)
q_range = []
Production dashboards are very effective in monitoring production over a given period of
time (Monthly, Daily , etc).Dashboards give insight and understanding to the operators of
what their field is performing. Python provides strong dashboarding capabilities. In this
example we use streamlit to create dashboard for VOLVE field data.
Libraries Used:
• Plotly Express.
• Streamlit
• Pandas
Plot Examples
A lot of missing data are present in oil and gas industry data warehouses, and the need to
be fixed before even making use of. This example deals with casing head pressure where in
contains tens of missing values.
Libraries Used:
• Plotly.
• Pandas
Plot Examples
It’s often desirable to generate network diagrams of well that are connected multiple
reservoir in same field, this will produce a full network of wells that are either tapping in a
single production formation of multiple formation at the same time.
Libraries Used:
• Pyvis.
• Random
Plot Examples
Python Code:
from pyvis.network import Network
import random
#Create a list of wells, target formation, water_production
wells = []
formations = []
production = []
#Setup Basic Network Canvas
net = Network(height=1000)
#Generate Random 100 Wells
for i in range(0, 100):
well_name = 'Well-{}'.format(random.randint(1, 100))
wells.append(well_name)
#Generate Random 5 Formations
for i in range(0, 100):
formation_index = random.randint(1, 5)
formation_name = 'Formation-AB-{}'.format(formation_index)
formations.append(formation_name)
#Generate 100 valus for water production
for prod in range(0,100):
production.append(random.random() * 1000)
#Setup basic formation Nodes
#Upcomming well nodes will connect to these 5 nodes
for f in formations:
colors = ['red', 'black','green','cyan','pink']
#Generate Color based on index of formation
index = int(f.split('-')[2])
color = colors[index-1]
net.add_node(f, f, color=color)
#Generate 100 Wells Nodes and connect
#them with assigned formation
for w,f,p in zip(wells, formations, production):
colors = ['red', 'black','green','cyan','pink']
color = colors[int(f.split('-')[2])-1]
net.add_node(w, w)
#Set width of connection edges, proportional to production
net.add_edge(w, f,color=color,width=p/250)
#Save the generated diagram
#Open it with web browser manually
net.show('example1.html')
This example will use same equation as example 1 with the aim of multiple plotting
plt.figure(figsize=(10,8))
#read data from multi well file...
gas_df = pd.read_csv('gasrate-mw.txt', sep='\t')
i = 0
for well in wells_list:
#filter data frame based on well name..
df_filtered = gas_df[ gas_df['Bore_Code'] == well ]
axs[i].set_title(well)
axs[i].set_xlabel('Time'); axs[i].set_ylabel('Q [mmscf/d]')
axs[i].plot(df_filtered['time_days'], df_filtered['gas_volume'], label=f'{well}')
#plot turner rate as well
axs[i].plot(df_filtered['time_days'], df_filtered['Turner Q'], label=f'Turner -
{well}', ls='-.')
axs[i].legend()
axs[i].grid()
i = i +1
#export file as pdf
fig.savefig('Multi well Turner.pdf')
Screenshot
Code:
import plotly.graph_objects as go
import pandas as pd
import plotly.io as pio
#Read Data
data = pd.read_csv("production_history.txt", sep="\t")
Required Libraries
• Matplotlib.
• Psapy.
Screenshot
Required Libraries
• gradio.
• pandas.
• Plotly.
Screenshot:
Code:
import gradio as gr
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
df = pd.read_csv("volvetxt.txt", sep="\t")
df.DATEPRD = pd.to_datetime(df.DATEPRD);
df = df.convert_dtypes()
df = df[ df["WELL_TYPE"] == "OP"]
df = df.fillna(0)
wells = df["Wellbore name"].unique()
Openhole section during drilling are made using drill bit, however the hole size is not exactly
the same diameter as bit diameter due to many reasons. It’s usually beneficial to know the
exact volume of openhole volume to account for different calculations like cement volume
required behind casing.
𝐵𝑖𝑡𝑆𝑖𝑧𝑒 2
𝑉𝑜𝑙𝑜𝑝𝑒𝑛ℎ𝑜𝑙𝑒 = 3.14 𝑥 ( ) 𝑥 𝐻𝑒𝑖𝑔ℎ𝑡
2
Required Data:
• Well log (las file)
• Bit size
• Depth
• Caliper readings
Libraries used:
• Plotly.
• Lasio.
Plot Example
Python Code:
# Import Libraries
import lasio
import plotly.graph_objects as go
import plotly
from plotly.subplots import make_subplots
las = lasio.read('caliper.las') # Import Sample Las File
#Creat new curve for caliper in inches
las['CAL-in'] = las['CAL']/(25.6)
#Create Volume curve
interval_step = las.well['step'].value
las['VOL'] = 3.14*las['CAL-in'] * las['CAL-in'] * interval_step /144
tracks = ['CAL-in','VOL','GR'] # Track we wish to plot
subplots = make_subplots(rows=1,cols=6,subplot_titles=tracks)
#Iterate through tracks
for t in tracks:
subplots.add_trace(go.Line(y=las.depth_m,x=las[t],name=t),row=1,col=tracks.index(t)+1)
#Calculate openhole section volume
volume_log = las['VOL'].sum()
#calculate theoretical section volume
depth_start = las.well['STRT'].value
depth_end =las.well['stop'].value
interval = depth_end - depth_start #net interval(in ft )
bit_size = las.well['bit'].value
volume_bit = (3.14 * (bit_size/2)**2 / 144 ) * interval
print("Volume from log = {0:.2f} , Volume From Bitsize
{1:.2f}".format(volume_log,volume_bit))
plotly.offline.plot(subplots)
Commercial grade software that are provided by companies like (Schlumberger, Haliburton,
etc) have the capability to plot and calculate trajectories for already existing wells or wells
that are being planned. Python offers a simple yet power full library that can do what is
mentioned above. The input files must be in xlsx file type and in the following column format :
Plot Example
Python Code:
Note This Example only runs in Jupyter Notebooks
import pandas as pd
# import profile
import well_profile as wp
#import 2 welss from excel sheet
well1 = wp.load('well2.xlsx')
well2= wp.load('well1.xlsx')
#create 3rd well by entering data
well3 = wp.get(3500,profile='J', kop=2000, eob=3000 , build_angle=30)
#Create 4th Vertical well
well4 = wp.get(5000,profile='V',set_start={'north':300,'east':200})
#plot all wells
well2.plot(add_well=[well1,well3,well4],style={'darkMode':True,'size':3}).show()
#get survey calculations for well3
survey_list = []
for i in range(1,3500,100):
survey_list.append(well3.get_point(i))#add single depth property to the list
#Create Pandas dataframe
df =pd.DataFrame(survey_list)
This example shows how to map multiple wells location on GIS map using plotly graphs.
Example Plot:
Python Code:
import pandas as pd
import plotly.express as ex
import plotly
#read data
xy_df = pd.read_csv('demoxy.txt',sep='\t')
fig =
ex.scatter_mapbox(xy_df,lat='Latitude',lon='Longitude',hover_name='WellboreID',zoom=13,
text='WellboreID',color='Wellbore',size='TotalDepth')
#Copy paste this part
fig.update_layout(
mapbox_style="white-bg",
mapbox_layers=[
{
"below": 'traces',
"sourcetype": "raster",
"sourceattribution": "United States Geological Survey",
"source": [
"https://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/{z}/
{y}/{x}"
]
},
{
"sourcetype": "raster",
"sourceattribution": "Government of Canada",
"source": ["https://geo.weather.gc.ca/geomet/?"
"SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&BBOX={bbox-epsg-
3857}&CRS=EPSG:3857"
"&WIDTH=1000&HEIGHT=1000&LAYERS=RADAR_1KM_RDBR&TILED=true&FORMAT=image/png"],
}
D Exponent is an empirical diagnostic method that is used to predict over pressurized zones
during drilling operation. It is used to indicate well control situations.
Example Plot
Python Code:
import pandas as pd
import numpy as np
import streamlit as st
import plotly.graph_objects as go
from plotly.subplots import make_subplots
import plotly.express as px
from dask import dataframe as dd
Fracture Gradient is one of the most important calculated parameters in drilling engineering
where it’s used to design and engineer wells and beyond. Fracture gradient can be
obtained in multiple ways, one of those is using correlations based on data from well logs.
Equations Used:
Overburden Gradient(OBG):
𝑧
𝜎𝑣 = ∫ 𝜌𝑧 ∗ 𝑔 ∗ ∆𝑧
0
𝜎𝑣 is the overburden stress, (Pa), ρ(z) is the bulk density log at depth z, (kg/m3), g, is the
constant of gravitational acceleration = 9.81 (m/s2), z is the depth at the depth of interest,
(m)
Plot Example
Python Code:
import lasio as ls; import pandas as pd; import numpy as np
import matplotlib.pyplot as plt
Chapter 4: Operational
NORSOK Corrosion model is an industry best practice for calculation CO2 induced corrosion
in Water/Gas Systems. The model relies multiple inputs. NORSOK M-506 2005 is used for the
basis of calculation. Main equation is shown below. However above standard should be
consulted for the companion equations
Required Data:
• Pressure.
• Temperature.
• Mixture Velocity.
• Mixture Density.
• Aqueous System pH.
• CO2 Properties
Plot Example
Python Code
#This python code written based on NORSOK M-506 Standard
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly
import plotly.graph_objects as go
from plotly.subplots import make_subplots
df = pd.read_csv('1.flowdata.txt',sep='\t')
pipe_diameter = 16.36 #inches , 0.41 meters
water_visc = 0.001# =1cp = 0.001 N s/m2
water_ph = 5.5
co2_percent = 2 # %
f_pH = 0.25 # pH function
#corrosion Cooficient fitting
temp = [5,15,20,40,60,80,90,120,150]
kt = [0.42,1.59,4.76,8.92,10.69,10,6.25,7.77,5.203]
kt_ft = np.poly1d(np.polyfit(temp,kt,deg=4))
temperature_prediction = list(range(1,150,1))
#Step 1 Calculate Renolds Number
df['Re'] = (df['VELOCITY(M/S)'] * df['Density [kg/m3]
']*(pipe_diameter*0.0254))/water_visc
#Step 2 : Calculate Friction factor (blasius)
df['ff'] = 0.3164*df['Re']**(-0.25)
#Step 3 : Calculate Wall Shear Stress
df['ss'] = 0.5 * df['Density [kg/m3] '] * (df['VELOCITY(M/S)']**2) * df['ff']
#Step 4: Calculate Co2 properties
df['co2_pp'] = co2_percent*0.01 * df['PT[PSIG]']/14.5 # pressure in bars
df['co2_a_fact'] = 10**((df['PT[PSIG]']/14.5)*(0.0031 - (1.4/(df['TM[c]']+273))))
df['co2_fug'] = df['co2_pp'] * df['co2_a_fact']
#Step 5 :Calculate corrosion rate mm/year
df['corr_rate'] = kt_ft(df['TM[c]']) * ((df['co2_fug'] ** 0.62)) * ((df['ss']/19) **
(0.146 + 0.0324 * np.log10( df['co2_fug'])) )* f_pH
#Create Array of Visualization Plots
https://www.peassociations.com/ peassociations.com +91 6205464268
Python For Oil & Gas
fig = make_subplots(rows=2,cols=2,
subplot_titles=(['Kt function','Flowline Temperature','Fluid Velocity
and Holdup','Corrosion Rate(mm/year)']))
#Create Kt factor fitting plot
fig.add_trace(go.Scatter(x=temp,y=kt,name='Kt function',mode='markers'),row=1,col=1)
fig.add_trace(go.Scatter(x=temperature_prediction,y=kt_ft(temperature_prediction),name='K
t fit'),row=1,col=1)
#Create pressure and temperature plots
fig.add_trace(go.Line(x=df['length [m]'],y=df['TM[c]'],name='Temperature'),row=1,col=2)
fig.add_trace(go.Line(x=df['length [m]'],y=df['LIQUIDHOL'],name='Liquid
Holdup'),row=2,col=1)
fig.add_trace(go.Line(x=df['length [m]'],y=df['corr_rate'],name='mm/year'),row=2,col=2)
#Change figure template
fig.update_layout(template='plotly_dark')
plotly.offline.plot(fig)
This example calculates/figures out flow regime type in a set of given well data using Taitel
and Duckler flow regime map.
Required Data:
• Liquid Holdup.
• Mass Rate.
• Temperature.
• Mixture Density.
• etc
Libraries used:
• Fluids.
• Plotly.
Plot Example
Python Code;
#read data
plotgrid = make_subplots(rows=2,cols=2,)
data = pd.read_csv('2.flowstabilitydata.txt', sep='\t')
data['type'] = 0
for i in range(0,len(data)):
mass = data['mass_rate_kg/s'][i]
gas_hol = data['gas_hol'][i]
rohl = data['rohl_kg/m3'][i]
gor = data['gor_scf/stb'][i]
wht_r = data['wht_c'][i] * 1.8 + 491
api = data['api'][i]
liq_visc = calculate_satoil_viscosity(gor,api,wht_r)
gas_visc = calculate_gas_viscosity(8.11,0.71,wht_r)
data['type'][i] = fd.Taitel_Dukler_regime(mass,1-gas_hol ,rohl, 8.1,
liq_visc,gas_visc,1.99/25.6,0)[0]
#using express plot features
fig = ex.scatter(x=data['mass_rate_kg/s'],
y=data['gor_scf/stb'],symbol=data['type'],color=data['type'],
text=data['well_name'],size=data['mass_rate_kg/s'],
title='Flow Regime Advisor',size_max=30,template='ggplot2',
labels={'x':"Mass Rate",'y':'GOR'})
plotly.offline.plot(fig)
This example illustrates the process of building ESP recommendation dashboard based on a
design rate and fluid properties. This example imports a full ESP catalog from a text
database.
𝐻𝐻𝑃
Efficiency : 𝑬𝒇𝒇𝒄𝒊𝒆𝒏𝒄𝒚𝒑𝒖𝒎𝒑 = 𝑥 100 %
𝐵𝐻𝑃
Required Data:
• Esp catalog.
• Design rate.
• Fluid Properties.
Libraries Used:
https://www.peassociations.com/ peassociations.com +91 6205464268
Python For Oil & Gas
• Plotly
Plot Example
Python Code:
from IPython.display import display, HTML
import pandas as pd
import plotly
import plotly.graph_objects as go
import plotly.express as ex
from plotly.subplots import make_subplots
#Open ESP Catalog
esp_df = pd.read_csv('3-esp_catalog.csv',sep=',')
#valuate HHP and efficiency %
rhol = 0.88#produced fluid density
esp_df['hhp'] = esp_df['flow_rate'] * esp_df['head_per_stage']*7.4*60*rhol/3956
#60 is conversion factor from sec to min, 7.4 conversion from cf to gal
esp_df['eff'] = 100 * esp_df['hhp'] / esp_df['power']
print('Unique Models : ', len(esp_df['model'].unique()))
#Get IPR Data and Create Desired Rate:
liquid_rate = 5000 # stbpd
pwf =3650 # psig
pres = 4900 # psig
#Calculate Maximumrate by Vogel
p_ratio = pwf / pres
qo_max_vogel = liquid_rate/(1-0.2*(p_ratio)-0.8*(p_ratio**2))
#Create lambda function for vogel
vogel_liq_rate = lambda pwf : (qo_max_vogel*
(1-0.2*(pwf/pres)-0.8*(((pwf/pres))**2)))
#Entered Desired Rate
design_rate = 8000 #stb
https://www.peassociations.com/ peassociations.com +91 6205464268
Python For Oil & Gas
#Create IPR
#Create pwf list (assumed)
pwf_list = list(range(0,pres,100))
liquid_rate_list = []
for pressure in pwf_list:
liquid_rate_list.append(vogel_liq_rate(pressure))#add value to liquid list
#ESP Pump Recommender
#Step 1 : Convert Design Rate to ft3/sec
design_rate_cfps = design_rate * 0.000065 #Conversion factor
#Step 2 : Filter ESP Models
esp_df_filter = esp_df[esp_df['max_rate']>design_rate_cfps ]
esp_df_filter = esp_df_filter[esp_df_filter['min_rate']<design_rate_cfps]
#Group data by model
esp_grouped = esp_df_filter.groupby('model')
#Suggested ESP models
esp_models = list(esp_grouped.groups.keys())
# Establish Plot Template
specss = specs=[[{"secondary_y": True}, {"secondary_y": True}],
[{"secondary_y": True}, {"secondary_y": True}]]
plot_fig = make_subplots(rows = 2, cols=2,specs=specss,subplot_titles=['IPR Based On
Latest Test',
'Pump Head Comparison',
'Pumps Models above 65%
Efficiency','Peformance Plot - Best Pump'],
x_title='Flow
Rate',horizontal_spacing=0.05,vertical_spacing=0.1)
top_pumps = {}
for model in esp_models:
esp_df_model = esp_df[esp_df['model'] == model]
#only consider efficiency factors above
if esp_df_model['eff'].max()<65:
continue ;
plot_fig.add_trace(go.Scatter(x=esp_df_model['flow_rate']/0.000065,
y=esp_df_model['head_per_stage'],name = model+"
"+'{:.0f} %'.format(esp_df_model['eff'].max())),
row=1,col=2)
top_pumps[model] = esp_df_model['eff'].max()
bar_plot = go.Bar(x=list(top_pumps.keys()),y=list(top_pumps.values()),name='Efficiency
Bars')
ipr_plot = go.Scatter(x=liquid_rate_list,y=pwf_list,name='IPR')
plot_fig.add_trace(ipr_plot,row=1,col=1)
#best performing pump
esp_best = esp_df[esp_df['model'] == esp_models[0]]
perf_plot1 =
go.Scatter(x=esp_best['flow_rate']/0.000065,y=esp_best['head_per_stage'],name='Head Per 1
Stage')
perf_plot2 =
go.Scatter(x=esp_best['flow_rate']/0.000065,y=esp_best['eff'],name='efficiency')
perf_plot3 = go.Scatter(x=esp_best['flow_rate']/0.000065,y=esp_best['hhp'],name='HHP')
perf_plot4 =
go.Scatter(x=esp_best['flow_rate']/0.000065,y=esp_best['power'],name='power')
plot_fig.add_trace(bar_plot,row=2,col=1)
plot_fig.add_trace(perf_plot1,row=2,col=2)
plot_fig.add_trace(perf_plot2,row=2,col=2)
plot_fig.add_trace(perf_plot3,row=2,col=2,secondary_y=True)
plot_fig.add_trace(perf_plot4,row=2,col=2,secondary_y=True)
#set plotting style
plot_fig.update_layout(template='ggplot2')
#plot
This example illustrates various techniques might be used to smooth wellhead pressure data
that is obtained from DOF projects on seconds/minute bases. Or the pressures that are
obtained from continuous monitoring on wellhead pressure during surface well tests.
Required Data:
• WHP.
Plot Example:
This example illustrates how to average and smooth production data and use Gradio as a
graphical user interface.
Screenshot
Code:
import gradio as gr
import plotly.express as px
import pandas as pd;import numpy as np
data = pd.read_csv("production_history.txt", sep="\t")
wells = list( data["UID"].unique() )
#Create a function to plot data and fit using Averaging, Polyfit
def plotting(well_name, variable):
df = data[ data["UID"] == well_name]
df["Moving Average"] = data[variable].rolling(10).mean()
polyfit = np.polyfit(df.index, df[variable], deg=2)
polyfunc = np.poly1d( polyfit )
y_fitted = polyfunc(df.index)
df["Polyfit"] = y_fitted
fig = px.line(df, x="Date", y=[variable, "Moving Average", "Polyfit" ])
return fig
#get selectable variables
variables = list(data.columns)
#make a dropdown for the
well_selector = gr.Dropdown(choices=wells, label="Please Select a Well",
value=wells[0])
var_selector = gr.Dropdown(choices=variables, label="Please Select a Variable",
value=variables[0], multiselect = False)
#Create the main Graphical User Interface
gui = gr.Interface(plotting, inputs=[well_selector, var_selector],
outputs=gr.Plot(), live=True)#make it Live=True to instantly
update when UI changes
https://www.peassociations.com/ peassociations.com +91 6205464268
Python For Oil & Gas
gui.launch(inline=True)
Example 6: Buy/Sell Prediction Using Mean Reversion
This example illustrates how to predict oil prices using mean reversion method that is widely
used in trading and quants used by Stock Price Analysts
The main idea behind this analysis is to compare historical data to a rolling average (with
window of 5 Years), Any price above the mean is potential for Sell, otherwise it’s a buy
period.
Screenshot
Code:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
%matplotlib qt
import datetime as dt
plt.style.use("seaborn-v0_8")
plt.Figure(figsize=(20, 10))
# read the data
brent = pd.read_csv("brent-daily.csv")
brent["Date"] = pd.to_datetime(brent.Date)
#plot the historical values
#plt.scatter(brent.Date, brent.Price, s=3, marker="_", color="black")
#create rolling average for Oil Prices
brent["5Yr avg"] = brent["Price"].rolling(1825).mean()
#create bands based on the average
https://www.peassociations.com/ peassociations.com +91 6205464268
Python For Oil & Gas
std = brent.Price.std()
brent["UpperBand"] = brent["Price"].rolling(1825).std() * 0.25 +
brent["Price"].rolling(1825).mean()
brent["LowerBand"] = brent["Price"].rolling(1825).mean() -
brent["Price"].rolling(1825).std() * 0.25
plt.grid(color="white");plt.xlabel("Date");plt.ylabel("Price [$]")
# Add Set of orizontal lines to denote max min,
plt.hlines(brent.Price.mean(),brent.Date.min(), brent.Date.max(), label="All Time
Mean")
plt.hlines(brent.Price.max(),brent.Date.min(), brent.Date.max(), label="All Time
High",color="green")
plt.hlines(brent.Price.min(),brent.Date.min(), brent.Date.max(), label="All Time
Low",color="red")
plt.grid(color="white")
plt.legend()
plt.title("Brent Oil Price Mean Reversion")
plt.savefig("plot.pdf")
plt.tight_layout()
Example Plot:
Standing Proposed the following correlation to calculate oil density at any give pressure and
temperature.
Density can be plotted agaist key influencing factors(e.g Gas contained in oil phase,
temperature or Dead Oil Density)
Plot Example:
api = 30
temperature_c = 90 #deg C
gas_spgr = 0.7 # From Lab Tests
solubility = 500 #scf/stb
figure = go.Figure()
#Creating a loop for effect of Pressure and temperature
temp_range = range(30,150,20)
gor_list = range(14,2500,20)
rho_list = []
for temp in temp_range:
for gor in gor_list:#loop through pressure range
temp_r = temp * 1.8 + 491.7
rho_list.append(calculate_oil_density(api,gas_spgr,gor,temp_r))
#plot curve @ given temperature
figure.add_trace(go.Scatter(x=list(gor_list),y=rho_list,name='Temperature =
'+str(temp_r)))
#Clear all items from the list
rho_list.clear()
#Update graph and show the plot
figure.update_layout(title='Standing - Oil Density ',template='seaborn'
,yaxis=dict(title='Oil Density lb/ft3'),xaxis=dict(title='GOR
(scf/stb)'))
plotly.offline.plot(figure)