Navigating Well log formats in the Volve Oilfield data with the Welly library

Petrophysical data of the Volve data is available in the Well_logs folder of the dataset. The data is available as raw and proccessed well log data files along with interpretation reports.

We will be using the Welly Python Library to import and process the Well logs. A newer well log format – the JSON well log – will used instead of the other formats and we’ll also provide a code snippet to integrate this format with the Welly library.

LAS log format

Log ASCII Standard (LAS) is a standard file-format very common in the petroleum industry to store well log information. A single LAS file can only contain data for one well, but it can contain any number of log curves from that well. 

Most of the well logs of the Volve oilfield are available in the LAS format. The LAS files can be read by opening them in a text editor.

DLIS log format

The Digital Log Interchange Standard (DLIS) format was introduced by the American Petroleum Institute (API) in 1991. The DLIS files stores data in a format consisting of frames, channel and sets which makes additional processing necessary. The format was optimized to store data in multiple magnetic tapes, a complexity no longer required. These are binary files and cannot be read without specialized software.

Erland M. Viggen provides an excellent explanation of the DLIS log format and the tools to access the information in it.

The Composite logs, Integrity Logs, Production logs of the Volve dataset are available in the DLIS format and cannot be read using tools available over the internet. The most efficient method to access these files is to use the JSON files provided by Petroware.

LIS log format

Log Information Standard (LIS) is the predecessor to DLIS and was developed by Schlumberger in the late 1970s. A large number of archived files still exist in this format. The files contain binary data and cannot be read with out specialized software.

The Mud logs and Composite logs of the Volve Oilfield are provided in this format. The most efficient method to access these files is to use the JSON files provided by Petroware.

ASC log format

ASC is a collective term used in the industry for denoting any ASCII based file that may contain well log information. Pattern recognition technologies are required to process the data in these files.

The Seismic check-shot and velocity data logs are provided in this format. The other ACS files in the dataset are header files of Well logs.

JSON log format

The JavaScript Object Notation (JSON) is a standard logging format used across industries, yet remains relatively new in the Oil and Gas industry. The format overcomes the deficiencies of the previous formats in terms of simplicity, massive transmission rates, cloud computing and big data analytics.

Petroware has generously converted all the Volve oilfield well log data to the JSON format which can be access in its directory . A detailed description of the JSON format data types, schema, objects is provided in its Github repository. Tools are provided to read the JSON file in Petrel, Ms Excel, Python, Java and Matlab.

An example of the JSON well log.

The Welly library

Welly is a python library that provides a high level interface for to the lasio library. It wraps a family of classes to facilitate users to work with objects like well, curve, segment that have little to do with the format of the log file. This simplifies the loading, processing, and analysis of sub-surface wells and well data. The documentation for the library can be accessed here.


The Github Python notebook for the code below can be accessed here. A YouTube video explaining this notebook is available here.

import numpy as np
import pandas as pd
from welly import Project, Well
import matplotlib.pyplot as plt

List of well logs in working directory

ls *.LAS
Volume in drive C is OS
 Volume Serial Number is 7041-7A2E

 Directory of C:\Users\Ankit Bansal\Desktop\Volve_log

10-09-2020  18:23         8,687,815 15_9-F-11A.LAS
10-09-2020  18:23        10,129,805 15_9-F-11B.LAS
10-09-2020  18:23        11,354,957 15_9-F-1A.LAS
10-09-2020  18:23        12,251,857 15_9-F-1B.LAS
10-09-2020  18:23        13,309,432 15_9-F-1C.LAS
               5 File(s)     55,733,866 bytes
               0 Dir(s)  77,406,220,288 bytes free

Create a Project

Import all the LAS files to create Project object. The output lists the time taken to import a log and also prints the well names of the logs.

p = Project.from_las('./*.las')
print(p)
0it [00:00, ?it/s]C:\ProgramData\Anaconda3\lib\site-packages\welly\well.py:173: FutureWarning: From v0.5 the default will be 'original', keeping whatever is used in the LAS file. If you want to force conversion to metres, change your code to use `index='m'`.
  warnings.warn(m, FutureWarning)
5it [00:10,  2.16s/it]
11A
11B
1A
1B
1C

Export data to a Pandas dataframe for scientific computing workflows

Pandas can provide a seamless integration into existing workflows and databases. However, the Welly library provides an intitutive way of working with well and logs rather instead of dataframes.

The output displays the well logs of well 11A from 2500m to 3000m in a Pandas dataframe.

df_wells = p.df()
df_wells.loc['11A'].query('Depth > 2500 & Depth < 3000')
ABDCQF01ABDCQF02ABDCQF03ABDCQF04BSCALIDRHODTDTSGRRACEHMRACELMRDRHOBRMROPRPCEHMRPCELMRTNBGRCFM
Depth
2500.12.1962.0901.7411.80417.5NaNNaNNaNNaN83.849NaNNaN0.753NaN0.78422.616NaNNaN0.784NaN
2500.22.1962.0901.7411.80417.5NaNNaNNaNNaN84.439NaNNaN0.767NaN0.80622.906NaNNaN0.806NaN
2500.32.1962.0901.7411.80417.5NaNNaNNaNNaN78.017NaNNaN0.774NaN0.79723.374NaNNaN0.797NaN
2500.42.1962.0901.7411.80417.5NaNNaNNaNNaN81.418NaNNaN0.763NaN0.77723.841NaNNaN0.777NaN
2500.52.1962.0901.7411.80417.5NaNNaNNaNNaN82.551NaNNaN0.768NaN0.77224.308NaNNaN0.772NaN
2999.62.5252.5262.5332.5168.58.6250.05269.980126.60110.3542.2702.4152.4312.5232.71323.9312.7132.4312.713NaN
2999.72.5322.5342.5322.5128.58.6250.05269.866127.96610.5252.2682.4052.4352.5252.74323.9392.7442.4352.743NaN
2999.82.5352.5452.5322.5088.58.6250.05369.788130.62810.6902.2852.4052.4492.5282.77823.9262.7782.4492.778NaN
2999.92.5352.5562.5362.5068.58.6250.05669.793132.66710.2672.2782.4082.4662.5322.78123.9132.7812.4662.781NaN
3000.02.5342.5622.5432.5108.58.6480.05969.863134.2829.686

Create a Well object from the project

A well object ‘well_11A’ is created from the Project. The output prints the header of the LAS file.

well_11A = p.get_well('11A')
well_11A
15/9-F-11 A
11A
tdNone
crsCRS({})
locationMaersk Inspirer
countryNorway
province
county
latitude058 26′ 29.956″ N DMS
longitude001 53′ 14.866″ E DMS
api
dataABDCQF01, ABDCQF02, ABDCQF03, ABDCQF04, BS, CALI, DRHO, DT, DTS, GR, NPHI, PEF, RACEHM, RACELM, RD, RHOB, RM, ROP, RPCEHM, RPCELM, RT

Plotting

Plot Gamma ray and NPHI-RHOB well logs

The object Segment stores a section of the well log. The section is defined by a basis which can either be the Depth or Time data. The depth of the Hugin formation in the 11A well is 3600m to 3700m. Gamma ray, Neutron Porosity, Density and Deep resistivity logs are segmented for analysis. The output displays the Gamma ray log and the overlay of the Density – Neutron Porosity logs. The well logs here are plotted using the Plotly library but other plotting libraries can also be easily integrated.

segment_GR = well_11A.data['GR'].to_basis(start=3590, stop=3710)
segment_NPHI = well_11A.data['NPHI'].to_basis(start=3590, stop=3710)
segment_RHOB = well_11A.data['RHOB'].to_basis(start=3590, stop=3710)
segment_RT = well_11A.data['RPCEHM'].to_basis(start=3590, stop=3710)

#Matplotlib plotly functions can be used with Welly objects
fig, axes = plt.subplots(nrows =1,ncols = 2,constrained_layout=True, figsize =(5, 10))

segment_GR.plot(ax = axes[0], c = 'g')
axes[0].set_xlim([0, 120])
axes[0].fill_betweenx(segment_GR.basis, 150,  segment_GR, hatch='///', edgecolor='green', facecolor='none')

ax1 = axes[1]
ax2 = ax1.twiny()
ax1.set_xlim([0.45, -0.15])
ax2.set_xlim([1.95, 2.95])
segment_NPHI.plot(ax=ax1, c = 'r')
segment_RHOB.plot(ax=ax2)
segment_2 = -segment_NPHI+2.6
segment_2.plot(ax=ax2, c = 'None')
ax2.fill_betweenx(segment_NPHI.basis, segment_RHOB, segment_2, color='red', alpha=0.5, where = (segment_2>segment_RHOB))

Plot all the Gamma Ray logs in a project

An important part of geologic interpretation is correlating logs from different wells. Here, the Gamma ray logs of all wells in the project are plotted.

Despiking was performed in the well log of well 1C. A median window filter available as a part of the Welly library is used.

fig, axs = plt.subplots(figsize=(9, 18), ncols=len(p)) 

for i, (ax, w) in enumerate(zip(axs, p)):
    gr = w.data['GR']
    if gr is not None:
        ax = gr.plot(ax=ax)
    ax.set_title("GR for\n{}".format(w.uwi))

#Show despiking operation
axs[4].clear()
g = p.get_well('1C').data['GR'].smooth(window_length=50, samples=True, func1d=np.median)
g.plot(ax=axs[4])

for ax in axs:
    ax.set_xlim([1, 150])
    ax.set_ylim([3000, 3500])
    ax.invert_yaxis()
    ax.xaxis.tick_top()
    ax.xaxis.set_label_position('top')
fig.tight_layout()

Post processing of well logs

Calculate the porosity, water saturation, permeability from the equations provided in the petrophysical interpretation report. Log segments have been used to calculate these properties.

#Calculate parameters for cross plot
#Calculate PHIF for well 11A
PHID = (2.65-segment_RHOB)/(2.65-0.9)
PHIF = PHID + 0.4*(segment_NPHI-PHID)+0.01

#Calculate Vsh for well 11A
VSH = (segment_GR-7)/(120-7)

#Calulate KLOGH
KLOGH = 10**(2+(8*PHIF)-(9*VSH))
#(2+8*PHIF−9*VSH)

#Calculate Swt fro well 11A
m = 1.865*(KLOGH**-0.0083)
SWt = 0.07/ ((PHIF**m)*segment_RT)

An industry standard cross plot of porosity vs permeability is plotted. The output indicates that most of sand has a permeability of around 100 mD. Sands with a permeability above 1000 mD indicate the presence of a thief zone.

#Make cross plot of PHIF and KLOGH
fig, axs = plt.subplots(figsize=(9, 7)) 
axs.scatter(PHIF[(PHIF>0.1) & (VSH<0.5)],KLOGH[(PHIF>0.1) & (VSH<0.5)], color = 'g', Edgecolor = 'k', alpha = 0.4)

axs.set_xlim([0,0.3])
axs.set_xlabel('PHIF (v/v)')
axs.set_ylabel('KLOGH (mD)')

Export the calculated parameter as a LAS file

The calculated properties from the well log are exported as a LAS file.

well_11A.data['KLOGH']=KLOGH
well_11A.data['PHIF']=PHIF
well_11A.data['VSH']=VSH
well_11A.to_las(fname='11A_output.LAS',keys = ['KLOGH', 'PHIF', 'VSH'])

Code Snippet to create Welly object from JSON files

Here, we import the JSON file and the extract the header, curve information and curve data. This is written to a temporary LAS file that is imported again as a Welly object. All classes, functions, data types in the Welly library are applicable to this Welly object.

import welly
import lasio
import json
import os

Well_name = welly.Well()
    well = from_JSON(Well_name, fname = FNAME)
def from_JSON(cls, fname, remap=None, funcs=None, data=True, req=None, alias=None, encoding=None, printfname=False, index=None):
        """
        Args:
            fname (str): The path of the LAS file, or a URL to one.
            remap (dict): Optional. A dict of 'old': 'new' LAS field names.
            funcs (dict): Optional. A dict of 'las field': function() for
                implementing a transform before loading. Can be a lambda.
            printfname (bool): prints filename before trying to load it, for
                debugging
            index (str): Optional. Either "existing" (use the index as found in
                the LAS file) or "m", "ft" to use lasio's conversion of the
                relevant index unit.

        Returns:
            the lasio object.
        """
        if printfname:
            print(fname)

       
        remap = {'STRT':'startIndex', 'STOP': 'endIndex', 'STEP':'step', 'NULL':'null', 'COMP': 'operator', 'WELL':'well', 'FLD':'field', 'LOC': 'location', 'PROV': 'province', 'CNTY':'county', 'STATE': 'state', 'CTRY': 'country', 'DATE':'date', 'SRVC':'serviceCompany', 'UWI':'uwi', 'API':'api', 'LATI':'latitude', 'LONG':'longitude', 'RUN':'runNumber',  'PD':'permanent datum', 'ELZ':'elevation log zero', }
        
        with open(fname, "r") as file:
            log = json.load(file)[0]
        
        l = lasio.LASFile()
        
        for key, value in l.well.items():
            item_to_fetch = remap.get(key)
            if item_to_fetch is None:
                continue
            try:
                setattr(l.well, key, log['header'][item_to_fetch])
            except:
                continue
        
        l.well.NULL = -999.25
               
        for count, item in enumerate(log['curves']):
            d = list(map(lambda row: row[count], log['data']))
            d = [-999.25 if v is None else v for v in d]
            l.insert_curve(ix = count, mnemonic = item['name'], data  = d , unit = item['unit'], descr = item['description'], value = None)
                
        fn = "temp.las"
        with open(fn, mode="w") as f: # Write LAS file to disk
            l.write(f)
    
        las = lasio.read(fn, encoding=encoding, null_policy='none')
        
        os.remove(r'temp.LAS')
        
        # Pass to other constructor.
        return cls.from_lasio (las, remap=remap, funcs=funcs, data=data, req=req, alias=alias, fname=fname, index=index)

A pull request for this code has been created for the Welly library on Github. We’ll update the code for any revisions and please contact us it you have any issues with it.

Subscribe for Updates and Conversations

3 thoughts on “Navigating Well log formats in the Volve Oilfield data with the Welly library”

Leave a Reply

Your email address will not be published. Required fields are marked *