Cegal Prizm Python Tool Pro - Well log operations#

Enabling data science against Petrel#

In this Notebook, we show how to retrieve well logs from a well, combine multiple logs into a single log, delete logs, apply a smoothing algorithm, and send data back to Petrel

Python libraries#

For this Notebook to work, additional Python libraries are needed.

[1]:
# This Notebook uses the following packages

import pandas as pd
from matplotlib import pyplot as plt
import ipywidgets as widgets
import os, sys
from random import sample
import scipy


#Packages used to connect to petrel and to access relevant data

from cegalprizm.pythontool import PetrelConnection
from cegalprizm.pythontool.welllog import WellLog, DiscreteWellLog

Connect to Petrel#

The first step is to establish a connection to Petrel. We will create a Python variable called ‘petrel’ which we will use for accessing the Petrel data.

[17]:
# Make sure you have Petrel open with the project you want to work on and run the following command

petrel = PetrelConnection(allow_experimental=True)

print('Connected to {}'.format(petrel.get_current_project_name()))
Connected to Gullfaks_dataset3.pet

Select a well and retrieve the well logs#

[3]:
# Method 1: selecting the desired well using a widget

well_names = sorted(list(petrel.wells.keys()))
well_name_widget = widgets.Dropdown(
    options=well_names,
    value=well_names[0],
    description='Select well:',
    disabled=False,
)
display(well_name_widget)
[4]:
# Method 2 : assign a particular well to a variable
well_list=sorted(list(petrel.wells.keys()))
well_C6= well_list[4]
print(well_C6)
Input/Wells/Injectors/C6
[5]:
#Method 1.1: Retrieve well logs using a widget
from random import sample
well = petrel.wells[well_name_widget.value]
cont_logs = [log for log in well.logs if type(log) is WellLog]
log_names = [log.petrel_name for log in cont_logs]
w = widgets.SelectMultiple(
    options=log_names,
    value=sample(log_names, min(len(log_names)//2, 6)),
    rows=len(log_names),
    description='Select logs',
)

display(w)
[6]:
#Method 2.2: Assign the logs to a variable
C6_contLogs=[log for log in well.logs if type(log) is WellLog]
print(C6_contLogs)
[WellLog(petrel_name="NetGross"), WellLog(petrel_name="Perm"), WellLog(petrel_name="GR1"), WellLog(petrel_name="GR12"), WellLog(petrel_name="GR13"), WellLog(petrel_name="Porosity"), WellLog(petrel_name="One-way time 1"), WellLog(petrel_name="Merged_GR")]

Getting all logs interpolated to the same depths in a Pandas DataFrame#

[7]:
logs_df = well.logs_dataframe(C6_contLogs[0:6])
# Sets MD as index
logs_df = logs_df.set_index('MD')
#logs_df = logs_df[list(C6_contLogs[1:5])]
logs_df.loc[2200:2500]
[7]:
NetGross Perm GR1 GR12 GR13 Porosity TWT TVD
MD
2200.222256 0.036268 104.219218 NaN 71.364325 NaN 0.091910 2008.914269 2195.166702
2200.722256 0.532327 116.146416 NaN 73.078747 NaN 0.090269 2009.222128 2195.615601
2201.222256 0.000000 137.262845 NaN 74.734042 NaN 0.088702 2009.529775 2196.064228
2201.722256 0.000000 169.863955 NaN 76.315481 NaN 0.088083 2009.837268 2196.512666
2202.222256 0.746174 199.066936 NaN 76.940353 NaN 0.088649 2010.144549 2196.960839
... ... ... ... ... ... ... ... ...
2471.222256 0.000000 81.100040 NaN NaN 80.895119 0.089047 2162.230573 2423.318593
2471.722256 0.000000 90.980329 NaN NaN 75.967539 NaN 2162.510717 2423.735403
2472.222256 0.000000 NaN NaN NaN NaN NaN 2162.790861 2424.152214
2472.722256 0.000000 NaN NaN NaN NaN NaN 2163.071006 2424.569024
2473.222256 0.000000 NaN NaN NaN NaN NaN 2163.351150 2424.985834

547 rows × 8 columns

Plotting logs#

[8]:
log_types = logs_df.columns
n = len(log_types)
fig, ax = plt.subplots(1, n-2, figsize=(2*n, 10), sharey = True)

for i in range(n-2):
    lt = log_types[i]
    ax[i].plot(logs_df[lt], logs_df.index, color='C'+str(i))
    ax[i].set_title(lt)
    ax[i].grid()

plt.gca().invert_yaxis()
../../../../_images/products_python-tool-pro_PythonToolProWorkbooks_Tutorials_WellLogs_Operations_14_0.png

Merging logs#

It looks like our GR-log is split into 3 log tracks. Let’s try and merge all 3 logs into a new GR log:

[9]:
log_types = logs_df.columns
n = len(log_types)
fig, ax = plt.subplots(1, n-5, figsize=(n, 15), sharey = True)

for i in range(n-5):
    lt = log_types[i+2]
    ax[i].plot(logs_df[lt], logs_df.index, color='C'+str(i))
    ax[i].set_title(lt)
    ax[i].grid()

plt.gca().invert_yaxis()
../../../../_images/products_python-tool-pro_PythonToolProWorkbooks_Tutorials_WellLogs_Operations_17_0.png

Let’s create a new column in our DataFrame called merged_GR and use the combine_first() function to combine the three logs:

[10]:
logs_df['merged_GR']=logs_df['GR1'].combine_first(logs_df['GR12'])
logs_df['merged_GR']=logs_df['merged_GR'].combine_first(logs_df['GR13'])
logs_df.loc[2200:2500]
[10]:
NetGross Perm GR1 GR12 GR13 Porosity TWT TVD merged_GR
MD
2200.222256 0.036268 104.219218 NaN 71.364325 NaN 0.091910 2008.914269 2195.166702 71.364325
2200.722256 0.532327 116.146416 NaN 73.078747 NaN 0.090269 2009.222128 2195.615601 73.078747
2201.222256 0.000000 137.262845 NaN 74.734042 NaN 0.088702 2009.529775 2196.064228 74.734042
2201.722256 0.000000 169.863955 NaN 76.315481 NaN 0.088083 2009.837268 2196.512666 76.315481
2202.222256 0.746174 199.066936 NaN 76.940353 NaN 0.088649 2010.144549 2196.960839 76.940353
... ... ... ... ... ... ... ... ... ...
2471.222256 0.000000 81.100040 NaN NaN 80.895119 0.089047 2162.230573 2423.318593 80.895119
2471.722256 0.000000 90.980329 NaN NaN 75.967539 NaN 2162.510717 2423.735403 75.967539
2472.222256 0.000000 NaN NaN NaN NaN NaN 2162.790861 2424.152214 NaN
2472.722256 0.000000 NaN NaN NaN NaN NaN 2163.071006 2424.569024 NaN
2473.222256 0.000000 NaN NaN NaN NaN NaN 2163.351150 2424.985834 NaN

547 rows × 9 columns

We can now display the new merged_GR log:

[11]:
log_types=logs_df.columns[[2,3,4,8]]
n=len(log_types)
fig, ax = plt.subplots(1, n, figsize=(8, 15), sharey = True)

for i in range(n):
    lt = log_types[i]
    ax[i].plot(logs_df[lt], logs_df.index, color='C'+str(i))
    ax[i].set_title(lt)
    ax[i].grid()

plt.gca().invert_yaxis()
../../../../_images/products_python-tool-pro_PythonToolProWorkbooks_Tutorials_WellLogs_Operations_21_0.png

Writing the result back to Petrel#

We can either overwrite an existing log or create a new log. We can create a new log by cloning the GR1 log (without cloning its values) and then pass the merged_GR values to the cloned log using set_values().

[14]:
# Clone the GR1 log
copy_GR1=C6_contLogs[2].clone('Merged_GR', copy_values=False)
# Assign the MD values to a variable
MD_index=logs_df.index.to_numpy()
# Assign the merged_GR values to a variable
MergedGR_values=logs_df['merged_GR'].to_numpy()
#Assign the MD and Merged_GR values to the new log
copy_GR1.set_values(MD_index,MergedGR_values)

The image below shows the 3 GR logs and the merged_GR logs in Petrel:

gr_logs.png

Delete logs#

Let’s select the 3 incomplete GR logs and delete them from the project. We can select the 3 GR logs using a widget:

[15]:
#Method 1.1: Retrieve well logs using a widget
from random import sample
well = petrel.wells[well_name_widget.value]
cont_logs = [log for log in well.logs if type(log) is WellLog]
log_names = [log.petrel_name for log in cont_logs]
w = widgets.SelectMultiple(
    options=log_names,
    value=sample(log_names, min(len(log_names)//2, 6)),
    rows=len(log_names),
    description='Select logs',
)

display(w)

To delete the logs, we can use a Petrel Workflow. First, we assign all the workflows in our project to the workflows variable. Then, using list slicing, we select the right workflow. Using a for loop we can pass each of the three logs as the input variable in our workflow and delete them. The image below shows how the workflow looks in Petrel:

workflow.png

[29]:
workflows = [workflow for workflow in petrel.workflows]
delete_workflow = workflows[5]
delete_workflow_input_variable = delete_workflow.input['object']
for log in range(len(w.value)):
    delete_workflow.run(args={delete_workflow_input_variable:petrel.global_well_logs['Input/Wells/Global well logs/Demo logs/'+w.value[log]]})

We can check the available logs in our well and make sure the logs are deleted:

[31]:
C6_contLogs=[log for log in well.logs if type(log) is WellLog]
for log in C6_contLogs:
    print (log.petrel_name)
NetGross
Perm
Porosity
One-way time 1
Merged_GR

Smoothing logs#

To smooth well logs, we can use a moving window gaussian filter on each column in the DataFrame.

[36]:
# update the df
logs_df = well.logs_dataframe(C6_contLogs)
# Sets MD as index
logs_df = logs_df.set_index('MD')

# Apply a moving window gaussian filter on each variable in the DataFrame.
smooth_logs_df = logs_df.rolling(80, win_type = 'gaussian', center = True).mean(std = 20)

#Plot the smoothed logs on top of the original logs
log_types=logs_df.columns[[0,1,2,4]]
n = len(log_types)
fig, ax = plt.subplots(1, n, figsize=(3*n, 10), sharey = True)

for i in range(n):
    lt = log_types[i]
    ax[i].plot(logs_df[lt], logs_df.index, color='C'+str(i), label = 'raw')
    ax[i].plot(smooth_logs_df[lt], smooth_logs_df.index, color='k', linewidth = 2, label = 'smoothed')
    ax[i].set_title(lt)
    ax[i].grid()
    ax[i].legend()

plt.gca().invert_yaxis()
../../../../_images/products_python-tool-pro_PythonToolProWorkbooks_Tutorials_WellLogs_Operations_34_0.png

Let’s clone each log and create an empty copy in Petrel:

[41]:
for i in range(n):
    old_log_path = well_name_widget.value + '/Well logs/Demo logs/' + log_types[i]
    old_log = petrel.well_logs[old_log_path]
    try:
        new_sm_log = old_log.clone(log_types[i]+'_smooth')  # create the clone if it doesn't already exist
    except Exception:
        new_sm_log = petrel.well_logs[old_log_path+'_smooth'] # get if it already exists
        new_sm_log.readonly = False
    print(old_log)
    print(new_sm_log)

WellLog(petrel_name="NetGross")
WellLog(petrel_name="NetGross_smooth")
WellLog(petrel_name="Perm")
WellLog(petrel_name="Perm_smooth")
WellLog(petrel_name="Porosity")
WellLog(petrel_name="Porosity_smooth")
WellLog(petrel_name="Merged_GR")
WellLog(petrel_name="Merged_GR_smooth")

We have now created 4 new logs in Petrel:

smoothempty.png

Insert values into well logs#

We can now write some values to our cloned logs:

[42]:
for i in range(n):
    md = smooth_logs_df.index.to_numpy()
    new_sm_values = smooth_logs_df[log_types[i]].to_numpy()
    old_log_path = well_name_widget.value + '/Well logs/' + log_types[i]
    old_log = petrel.well_logs[old_log_path+'_smooth']
    new_sm_log = old_log
    new_sm_log.readonly = False
    new_sm_log.set_values(md, new_sm_values)

smoothfilled.png