Well log and petrophysical data come in several data formats. In many of my articles I have shared, we have mainly worked with CSV and LAS files. These formats are simple and easy to work with due to their flat structure. However, these files work well for simple logs, but not for array data. In LAS and CSV files, arrays get split into multiple columns rather than stored as a single block. DLIS files were designed to handle this complexity.

What is DLIS?

The Digital Log Interchange Standard (DLIS) is a structured binary format for storing well information and log data. It was developed by Schlumberger in the late 1980s and later published by the American Petroleum Institute in 1991 to provide a standardised format.

Working with DLIS can be awkward. The standard is decades old, and different vendors often add their own twists with extra data structures or object types.

A DLIS file typically holds large amounts of well metadata along with the actual log data. The data itself lives inside Frames, these are table-like objects representing passes, runs, or processing stages (e.g. Raw or Interpreted). Each frame has columns called channels, which are the individual logging curves. Channels can be single- or multi-dimensional, depending on the tool and measurement.

Installing DLISIO

DLISIO is a python library that has been developed by Equinor ASA to read DLIS files and Log Information Standard79 (LIS79) files. Details of the library can be found here.

The library can be installed by using the following command:

pip install dlisio

Opening a DLIS File

Like most binary formats, you can’t just open a DLIS file in a text editor and scroll through the contents like we can with las and csv files. DLISIO handles the decoding of the binary file for you.

The first step when working with DLISIO is to load the file and check what’s inside.

from dlisio import dlis
with dlis.load("NLOG_LIS_LAS_7857_FMS_DSI_MAIN_LOG.DLIS") as (logical_files, *tail):
    print(logical_files)
    print(logical_files.describe())

A DLIS file can very often contain one or more logical files. So it is always good practice to check to see what is within them.

Exploring frames

Each logical file is organised into frames. You can think of a frame as a table that stores log data from a particular pass, run, or processing stage. For example, you might have one frame for the raw field data and another for an interpreted or processed version of the same run.

Let’s take the first logical file and look at its frames:

with dlis.load("NLOG_LIS_LAS_7857_FMS_DSI_MAIN_LOG.DLIS") as (logical_files, *tail):
    for frame in logical_files.frames:
        print(frame.describe())

This will list all the frames available. A file might only have one frame, but it’s common to see several like in the example below. Understanding which frame you need is an important first step before pulling out data.

By using the describe() method on the frame we are able to see all of the contents like below.

-----
Frame
-----
name   : 60B
origin : 41
copy   : 0
Channel indexing
--
Indexed by       : BOREHOLE-DEPTH
Index units      : 0.1 in
Index min        : 0 [0.1 in]
Index max        : 0 [0.1 in]
Direction        : DECREASING
Constant spacing : -60 [0.1 in]
Index channel    : Channel(TDEP)
Channels
--
TDEP      BS        CS        TENS      ETIM      DEVI      P1AZ_MEST ANOR
FINC      HAZI      P1AZ      RB        SDEV      GAT       GMT       ECGR
ITT       SPHI      DCI2      DCI4      SOBS      DTCO      DTSM      PR
VPVS      CHR2      DT2R      DTRP      CHRP      DTRS      CHRS      DTTP
CHTP      DTTS      CHTS      DT2       DT4P      DT4S      SPCF      DPTR
DPAZ      QUAF      DDIP      DDA       FCD       HDAR      RGR       TIME
CVEL      MSW1      MSW2      FNOR      SAS2      SAS4      PWF2      PWN2
PWF4      PWN4      SVEL      SSVE      SPR2      SPR4      SPT4      DF
CDF       CLOS      ED        ND        TVDE      VSEC      CWEL      AREA
AFCD      ABS       IHV       ICV       GR
-----
Frame
-----
name   : 10B
origin : 41
copy   : 0
Channel indexing
--
Indexed by       : BOREHOLE-DEPTH
Index units      : 0.1 in
Index min        : 0 [0.1 in]
Index max        : 0 [0.1 in]
Direction        : DECREASING
Constant spacing : -10 [0.1 in]
Index channel    : Channel(TDEP)
Channels
--
TDEP IDWD TIME SCD
-----
Frame
-----
name   : 1B
origin : 41
copy   : 0
Channel indexing
--
Indexed by       : BOREHOLE-DEPTH
Index units      : 0.1 in
Index min        : 0 [0.1 in]
Index max        : 0 [0.1 in]
Direction        : DECREASING
Constant spacing : -1 [0.1 in]
Index channel    : Channel(TDEP)
Channels
--
TDEP  TIME  EV    BA28  BA17  BB17  BC13  BD13  BB28  BA13  BB13  BC17  BD17
BA22  BA23  BA24  BC28  BA25  BA26  BA27  BA11  BA12  BA14  BA15  BA16  BA18
BA21  BC11  BC12  BC14  BC15  BC16  BC18  BC21  BC22  BC23  BC24  BC25  BC26
BC27  BB22  BB23  BB24  BD28  BB25  BB26  BB27  BB11  BB12  BB14  BB15  BB16
BB18  BB21  BD11  BD12  BD14  BD15  BD16  BD18  BD21  BD22  BD23  BD24  BD25
BD26  BD27  SB1   DB1   DB2   DB3A  DB4A  SB2   DB1A  DB2A  DB3   DB4   FCAX
FCAY  FCAZ  FTIM  AZSNG AZS1G AZS2G
-----
Frame
-----
name   : 15B
origin : 41
copy   : 0
Channel indexing
--
Indexed by       : BOREHOLE-DEPTH
Index units      : 0.1 in
Index min        : 0 [0.1 in]
Index max        : 0 [0.1 in]
Direction        : DECREASING
Constant spacing : -15 [0.1 in]
Index channel    : Channel(TDEP)
Channels
--
TDEP   TIME   C1     C2     U-MBAV AX     AY     AZ     EI     FX     FY     FZ

Inspecting channels

Within each frame you’ll find the actual channels — the individual logging curves such as GR, RHOB, NPHI, and so on. Each channel is stored as a column in the frame’s table, with values indexed by depth or time.

Here’s how to list them:

with dlis.load("NLOG_LIS_LAS_7857_FMS_DSI_MAIN_LOG.DLIS") as (logical_files, *tail):
    for frame in logical_files.frames:
        for channel in frame.channels:
            print(channel.describe())

This will give you the names of the channels and their measurement units. Just seeing this list is useful: it tells you what curves are available before you start extracting values. It’s also a quick way to check if the file contains the curves you expect.

The output from the above can be quite lengthy, especially if you have several frames. The example below is an overview of what cam ne obtained using the above:

-------
Channel
-------
name   : BC12
origin : 41
copy   : 0
Description : CALIBRATED DATA BUTTON C12
Sample dimensions         : 1
Maximum sample dimensions : 1
Property indicators       : 440-CUSTOMER
Source                    : Tool(MESTB)

-------
Channel
-------
name   : BC14
origin : 41
copy   : 0
Description : CALIBRATED DATA BUTTON C14
Sample dimensions         : 1
Maximum sample dimensions : 1
Property indicators       : 440-CUSTOMER
Source                    : Tool(MESTB)

Summary

In this article we have opened a DLIS file and looked inside to check for logical files, explored the frames, and listed the channels contained in those frames.

In summary:

  • Logical files: the top-level containers.
  • Frames: table-like objects, often one per run or processing stage.
  • Channels: the individual log curves inside each frame.

In the next article we’ll go a step further and load the actual channel data into a pandas DataFrame, with units and metadata carried along. That’s where the DLIS format really pays off, giving you tidy arrays ready for analysis or plotting.