Read/write well data from Log ASCII Standard (LAS) files
Project description
This is a Python package to read and write Log ASCII Standard (LAS) files, used for borehole/well data (e.g. geophysical/geological/petrophysical logs). It is compatible with versions 1.2 and 2.0 of the LAS file specification, published by the Canadian Well Logging Society. In principle it is designed to read as many types of LAS files as possible, including ones containing common errors or non-compliant formatting.
It is written entirely in Python and works on any platform. It depends on the following packages available on PyPI:
Install
To install from PyPI use:
$ pip install lasio
If necessary this will download and install the package dependencies listed above.
Alternatively if you would like the latest version (which may contain bugs and errors) make sure you have setuptools and git installed and then use:
$ git clone https://github.com/kinverarity1/lasio.git
$ cd lasio
$ python setup.py develop
How to use
Look at the example IPython notebooks here. More detailed examples are coming.
Opening LAS files
To open a LAS file from disk:
>>> import lasio
>>> l = lasio.read("sample_big.las")
To open a LAS file from a URL:
>>> l = lasio.read("http://someplace.com/example.las")
Getting curve data
>>> from pprint import pprint
>>> pprint(l.curves)
[Curve(mnemonic=u'DEPT', unit=u'M', value=u'', descr=u'1 DEPTH', data=array([ 1670. , 1669.875, 1669.75 , ..., 1669.75 , 1670. , 1669.875])),
Curve(mnemonic=u'DT', unit=u'US/M', value=u'', descr=u'2 SONIC TRANSIT TIME', data=array([ 123.45, 123.45, 123.45, ..., 123.45, 123.45, 123.45])),
Curve(mnemonic=u'RHOB', unit=u'K/M3', value=u'', descr=u'3 BULK DENSITY', data=array([ 2550., 2550., 2550., ..., 2550., 2550., 2550.])),
Curve(mnemonic=u'NPHI', unit=u'V/V', value=u'', descr=u'4 NEUTRON POROSITY', data=array([ 0.45, 0.45, 0.45, ..., 0.45, 0.45, 0.45])),
Curve(mnemonic=u'SFLU', unit=u'OHMM', value=u'', descr=u'5 RXO RESISTIVITY', data=array([ 123.45, 123.45, 123.45, ..., 123.45, 123.45, 123.45])),
Curve(mnemonic=u'SFLA', unit=u'OHMM', value=u'', descr=u'6 SHALLOW RESISTIVITY', data=array([ 123.45, 123.45, 123.45, ..., 123.45, 123.45, 123.45])),
Curve(mnemonic=u'ILM', unit=u'OHMM', value=u'', descr=u'7 MEDIUM RESISTIVITY', data=array([ 110.2, 110.2, 110.2, ..., 110.2, 110.2, 110.2])),
Curve(mnemonic=u'ILD', unit=u'OHMM', value=u'', descr=u'8 DEEP RESISTIVITY', data=array([ 105.6, 105.6, 105.6, ..., 105.6, 105.6, 105.6]))]
>>> for curve in l.curves:
... print("%s\t[%s]\t%s\t%s" % (
... curve.mnemonic, curve.unit, curve.value, curve.descr))
...
DEPT [M] 1 DEPTH
DT [US/M] 2 SONIC TRANSIT TIME
RHOB [K/M3] 3 BULK DENSITY
NPHI [V/V] 4 NEUTRON POROSITY
SFLU [OHMM] 5 RXO RESISTIVITY
SFLA [OHMM] 6 SHALLOW RESISTIVITY
ILM [OHMM] 7 MEDIUM RESISTIVITY
ILD [OHMM] 8 DEEP RESISTIVITY
And if all you want is the curve data itself it’s accessible as items of the LASFile object:
>>> l["SFLU"]
array([ 123.45, 123.45, 123.45, ..., 123.45, 123.45, 123.45])
>>> l["DEPT"]
array([ 1670. , 1669.875, 1669.75 , ..., 1669.75 , 1670. , 1669.875])
Reading header information
Header information is parsed into simple HeaderItem objects, and stored in a dictionary for each section of the header:
>>> l = lasio.read("sample_big.las")
>>> l.version
{'VERS': HeaderItem(mnemonic=u'VERS', unit=u'', value=1.2, descr=u'CWLS LOG ASCII STANDARD -VERSION 1.2'),
'WRAP': HeaderItem(mnemonic=u'WRAP', unit=u'', value=u'NO', descr=u'ONE LINE PER DEPTH STEP')}
>>> l.well
{'STRT': HeaderItem(mnemonic=u'STRT', unit=u'M', value=1670.0, descr=u''),
'STOP': HeaderItem(mnemonic=u'STOP', unit=u'M', value=1660.0, descr=u''),
'STEP': HeaderItem(mnemonic=u'STEP', unit=u'M', value=-0.125, descr=u''),
'NULL': HeaderItem(mnemonic=u'NULL', unit=u'', value=-999.25, descr=u''),
'COMP': HeaderItem(mnemonic=u'COMP', unit=u'', value=u'ANY OIL COMPANY LTD.', descr=u'COMPANY'),
'WELL': HeaderItem(mnemonic=u'WELL', unit=u'', value=u'ANY ET AL OIL WELL #12', descr=u'WELL'),
'FLD': HeaderItem(mnemonic=u'FLD', unit=u'', value=u'EDAM', descr=u'FIELD'),
'LOC': HeaderItem(mnemonic=u'LOC', unit=u'', value=u'A9-16-49-20W3M', descr=u'LOCATION'),
'PROV': HeaderItem(mnemonic=u'PROV', unit=u'', value=u'SASKATCHEWAN', descr=u'PROVINCE'),
'SRVC': HeaderItem(mnemonic=u'SRVC', unit=u'', value=u'ANY LOGGING COMPANY LTD.', descr=u'SERVICE COMPANY'),
'DATE': HeaderItem(mnemonic=u'DATE', unit=u'', value=u'25-DEC-1988', descr=u'LOG DATE'),
'UWI': HeaderItem(mnemonic=u'UWI', unit=u'', value=u'100091604920W300', descr=u'UNIQUE WELL ID')}
>>> l.params
{'BHT': HeaderItem(mnemonic=u'BHT', unit=u'DEGC', value=35.5, descr=u'BOTTOM HOLE TEMPERATURE'),
'BS': HeaderItem(mnemonic=u'BS', unit=u'MM', value=200.0, descr=u'BIT SIZE'),
'FD': HeaderItem(mnemonic=u'FD', unit=u'K/M3', value=1000.0, descr=u'FLUID DENSITY'),
'MATR': HeaderItem(mnemonic=u'MATR', unit=u'', value=0.0, descr=u'NEUTRON MATRIX(0=LIME,1=SAND,2=DOLO)'),
'MDEN': HeaderItem(mnemonic=u'MDEN', unit=u'', value=2710.0, descr=u'LOGGING MATRIX DENSITY'),
'RMF': HeaderItem(mnemonic=u'RMF', unit=u'OHMM', value=0.216, descr=u'MUD FILTRATE RESISTIVITY'),
'DFD': HeaderItem(mnemonic=u'DFD', unit=u'K/M3', value=1525.0, descr=u'DRILL FLUID DENSITY')}
The ~Other section is stored as free text:
>>> l.other
u'Note: The logging tools became stuck at 625 meters causing the data\nbetween 625 meters and 615 meters to be invalid.'
The actual values are stored as the value attribute:
>>> l.well["UWI"].value
u'100091604920W300'
>>> l.well["DATE"].value
u'25-DEC-1988'
>>> l.params["BHT"].value
35.5
Creating a LAS file from scratch
First create an empty LASFile object:
>>> l = lasio.LASFile()
>>> l.header
{'~V': {'VERS': HeaderItem(mnemonic='VERS', unit='', value=2.0, descr='CWLS log ASCII Standard -VERSION 2.0'),
'WRAP': HeaderItem(mnemonic='WRAP', unit='', value='NO', descr='One line per depth step'),
'DLM': HeaderItem(mnemonic='DLM', unit='', value='SPACE', descr='Column Data Section Delimiter')},
'~W': {'STRT': HeaderItem(mnemonic='STRT', unit='m', value=nan, descr='START DEPTH'),
'STOP': HeaderItem(mnemonic='STOP', unit='m', value=nan, descr='STOP DEPTH'),
'STEP': HeaderItem(mnemonic='STEP', unit='m', value=nan, descr='STEP'),
'NULL': HeaderItem(mnemonic='NULL', unit='', value=-9999.25, descr='NULL VALUE'),
'COMP': HeaderItem(mnemonic='COMP', unit='', value='', descr='COMPANY'),
'WELL': HeaderItem(mnemonic='WELL', unit='', value='', descr='WELL'),
'FLD': HeaderItem(mnemonic='FLD', unit='', value='', descr='FIELD'),
'LOC': HeaderItem(mnemonic='LOC', unit='', value='', descr='LOCATION'),
'PROV': HeaderItem(mnemonic='PROV', unit='', value='', descr='PROVINCE'),
'CNTY': HeaderItem(mnemonic='CNTY', unit='', value='', descr='COUNTY'),
'STAT': HeaderItem(mnemonic='STAT', unit='', value='', descr='STATE'),
'CTRY': HeaderItem(mnemonic='CTRY', unit='', value='', descr='COUNTRY'),
'SRVC': HeaderItem(mnemonic='SRVC', unit='', value='', descr='SERVICE COMPANY'),
'DATE': HeaderItem(mnemonic='DATE', unit='', value='', descr='DATE'),
'UWI': HeaderItem(mnemonic='UWI', unit='', value='', descr='UNIQUE WELL ID'),
'API': HeaderItem(mnemonic='API', unit='', value='', descr='API NUMBER')},
'~C': [],
'~P': {},
'~O': }
Then let’s add some header items and curve data:
>>> import datetime
>>> l.well["DATE"].value = str(datetime.datetime.today())
>>> l.params["ENGI"] = lasio.HeaderItem("ENGI", "", "kinverarity@hotmail.com", "Creator of this file...")
>>> l.other = "Example of creating a LAS file from scratch using lasio"
>>> depth = [100, 100.5, 101, 101.5, 102]
>>> data = [5, 6, 9, 7, -9999.25]
>>> l.add_curve("DEPT", depth, unit="m")
>>> l.add_curve("DAT", data, descr="Made-up curve data for example")
Finally write the result (in this case to the console):
>>> import sys
>>> l.write(sys.stdout, version=2.0)
~Version ---------------------------------------------------
VERS. 2.0 : CWLS log ASCII Standard -VERSION 2.0
WRAP. NO : One line per depth step
DLM . SPACE : Column Data Section Delimiter
~Well ------------------------------------------------------
STRT.m 100.0 : START DEPTH
STOP.m 102.0 : STOP DEPTH
STEP.m 0.5 : STEP
NULL. -9999.25 : NULL VALUE
COMP. : COMPANY
WELL. : WELL
FLD . : FIELD
LOC . : LOCATION
PROV. : PROVINCE
CNTY. : COUNTY
STAT. : STATE
CTRY. : COUNTRY
SRVC. : SERVICE COMPANY
DATE. 2015-08-09 17:12:52.371000 : DATE
UWI . : UNIQUE WELL ID
API . : API NUMBER
~Curves ----------------------------------------------------
DEPT.m :
DAT . : Made-up curve data for example
~Params ----------------------------------------------------
ENGI. kinverarity@hotmail.com : Creator of this file...
~Other -----------------------------------------------------
Example of creating a LAS file from scratch using lasio
~ASCII -----------------------------------------------------
100 5
100.5 6
101 9
101.5 7
102 -9999.2
Character encodings
Three options:
Do nothing and hope for no errors.
Specify the encoding (internally lasio uses the open function from codecs which is part of the standard library):
>>> l = lasio.read("example.las", encoding="windows-1252")
Install a third-party package like cChardet (faster) or chardet (slower) to automatically detect the character encoding. If these packages are installed this code will use whichever is faster:
>>> l = lasio.read("example.las", autodetect_encoding=True)
Note that by default autodetect_encoding=False.
Development
0.7 (2015-08-08) - all tests passing on Python 2.6 through 3.4
0.6 (2015-08-05) - bugfixes and renamed from las_reader to lasio
0.5 (2015-08-01) - Improvements to writing LAS files
0.4 (2015-07-26) - Improved handling of character encodings, other internal improvements
0.3 (2015-07-23) - Added Python 3 support, now reads LAS 1.2 and 2.0
0.2 (2015-07-08) - Tidied code and published on PyPI
Contributions
Contributions are very welcome. Please fork the project on GitHub and submit a pull request (PR) containing any changes you have made.
Suggested improvements, bug reports, shortcomings, desirable features, examples of LAS files which do not load as you expected, are all also welcome either via GitHub or email.
Thanks to the following people in chronological order for their help:
@VelizarVESSELINOV
@diverdude
License
The code is freely available for any kind of use or modification under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Hashes for lasio-0.7.3-py2.py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 357d18dff83dba99815f558b3143673cf4a4caba0fe3c3cd7d56a05d73807cf5 |
|
MD5 | 9dbd143e4b41aa20d29a71be86eb944e |
|
BLAKE2b-256 | cad631617902e8d5d17f65dcc26024832ac8b24113fea672afee13407e0087c5 |