import os
from io import StringIO
import numpy as np
import pytest
import las_rs
test_dir = os.path.dirname(__file__)
def fixture(*parts):
return os.path.join(test_dir, "fixtures", *parts)
_LAS_BASIC = """\
~VERSION INFORMATION
VERS. 2.0 : LAS VERSION 2.0
WRAP. NO : ONE LINE PER DEPTH STEP
~WELL INFORMATION
STRT.M 200.0 : START DEPTH
STOP.M 202.0 : STOP DEPTH
STEP.M 1.0 : STEP VALUE
NULL. -999.25 : NULL VALUE
COMP. IRONCLAD PETROLEUM : COMPANY
WELL. FALCON-3 #1 : WELL NAME
~CURVE INFORMATION
DEPT.M : MEASURED DEPTH
GR .GAPI : GAMMA RAY
~ASCII LOG DATA
200.0 44.10
201.0 67.30
202.0 52.80
"""
_LAS_DODGY_HEADER = """\
~VERSION INFORMATION
VERS. 2.0 : LAS VERSION 2.0
WRAP. NO : ONE LINE PER DEPTH STEP
~WELL INFORMATION
STRT.M 10.0 : START DEPTH
STOP.M 11.0 : STOP DEPTH
STEP.M 1.0 : STEP VALUE
NULL. -999.25 : NULL VALUE
WELL. ERR-WELL : WELL NAME
~PARAMETER INFORMATION
!!!INVALID_PARAMETER_LINE_NO_DELIMITERS!!!
BHT .DEGC 95.0 : Bottom Hole Temperature
~CURVE INFORMATION
DEPT.M : DEPTH
GR .GAPI : GAMMA RAY
~ASCII LOG DATA
10.0 55.0
11.0 60.0
"""
_LAS_DATA_COMMENTS = """\
~VERSION INFORMATION
VERS. 2.0 : LAS VERSION 2.0
WRAP. NO : ONE LINE PER DEPTH STEP
~WELL INFORMATION
STRT.M 300.0 : START DEPTH
STOP.M 302.0 : STOP DEPTH
STEP.M 1.0 : STEP VALUE
NULL. -999.25 : NULL VALUE
WELL. COMMENT-WELL : WELL NAME
~CURVE INFORMATION
DEPT.M : DEPTH
GR .GAPI : GAMMA RAY
~ASCII LOG DATA
# This line is a data-section comment
300.0 30.0
# Another comment between data rows
301.0 35.0
302.0 40.0
"""
_LAS_QUOTE_COMMENTS = """\
~VERSION INFORMATION
VERS. 2.0 : LAS VERSION 2.0
WRAP. NO : ONE LINE PER DEPTH STEP
~WELL INFORMATION
STRT.M 400.0 : START DEPTH
STOP.M 401.0 : STOP DEPTH
STEP.M 1.0 : STEP VALUE
NULL. -999.25 : NULL VALUE
WELL. QCMT-WELL : WELL NAME
~CURVE INFORMATION
DEPT.M : DEPTH
GR .GAPI : GAMMA RAY
~ASCII LOG DATA
" This line starts with a double-quote and should be ignored
400.0 22.0
401.0 25.0
"""
_LAS_COMMA_DECIMAL = """\
~VERSION INFORMATION
VERS. 2.0 : LAS VERSION 2.0
WRAP. NO : ONE LINE PER DEPTH STEP
~WELL INFORMATION
STRT.M 1671.0 : START DEPTH
STOP.M 1673.0 : STOP DEPTH
STEP.M 1.0 : STEP VALUE
NULL. -999,25 : NULL VALUE
WELL. COMMA-1 : WELL NAME
~CURVE INFORMATION
DEPT.M : DEPTH
GR .GAPI : GAMMA RAY
~ASCII LOG DATA
1671,000 45,0
1672,000 50,5
1673,000 55,2
"""
_LAS_WRAPPED = """\
~VERSION INFORMATION
VERS. 2.0 : LAS VERSION 2.0
WRAP. YES : WRAPPED DATA
~WELL INFORMATION
STRT.M 500.0 : START DEPTH
STOP.M 501.0 : STOP DEPTH
STEP.M 1.0 : STEP VALUE
NULL. -999.25 : NULL VALUE
WELL. WRAP-WELL : WELL NAME
~CURVE INFORMATION
DEPT.M : DEPTH
GR .GAPI : GAMMA RAY
DT .US/M : SONIC
RHOB.G/CC : DENSITY
NPHI.V/V : NEUTRON
CALI.IN : CALIPER
~ASCII LOG DATA
500.0
38.5 75.2 2.441 0.312 8.62
501.0
42.1 80.0 2.510 0.290 8.50
"""
_LAS_MIXEDCASE = """\
~VERSION INFORMATION
VERS. 2.0 : LAS VERSION 2.0
WRAP. NO : ONE LINE PER DEPTH STEP
~WELL INFORMATION
STRT.M 50.0 : START DEPTH
STOP.M 51.0 : STOP DEPTH
STEP.M 1.0 : STEP VALUE
NULL. -999.25 : NULL VALUE
WELL. MIX-WELL : WELL NAME
~CURVE INFORMATION
Dept.M : Depth
Gr .GAPI : Gamma Ray
~ASCII LOG DATA
50.0 80.0
51.0 85.0
"""
@pytest.mark.xfail(reason="not yet implemented")
def test_ignore_data_true():
las = las_rs.read(_LAS_BASIC, ignore_data=True)
assert isinstance(las, las_rs.LASFile)
assert las.data.size == 0 or las.data.shape[0] == 0
assert las.well["COMP"].value is not None
assert len(las.curves) > 0
@pytest.mark.xfail(reason="not yet implemented")
def test_ignore_data_false():
las = las_rs.read(_LAS_BASIC, ignore_data=False)
assert las.data.shape[0] == 3
assert las.curves["GR"].data[0] == pytest.approx(44.10)
@pytest.mark.xfail(reason="not yet implemented")
def test_engine_numpy():
las = las_rs.read(_LAS_BASIC, engine="numpy")
gr = las.curves["GR"].data
assert isinstance(gr, np.ndarray)
assert gr[1] == pytest.approx(67.30)
@pytest.mark.xfail(reason="not yet implemented")
def test_engine_normal():
las_n = las_rs.read(_LAS_BASIC, engine="numpy")
las_r = las_rs.read(_LAS_BASIC, engine="normal")
np.testing.assert_array_almost_equal(
las_n.curves["GR"].data,
las_r.curves["GR"].data,
decimal=4,
)
@pytest.mark.xfail(reason="not yet implemented")
def test_use_normal_engine_for_wrapped():
las = las_rs.read(
_LAS_WRAPPED,
use_normal_engine_for_wrapped=True,
)
dept = las.curves["DEPT"].data
assert dept[0] == pytest.approx(500.0)
assert dept[1] == pytest.approx(501.0)
@pytest.mark.xfail(reason="not yet implemented")
def test_mnemonic_case_upper():
las = las_rs.read(_LAS_MIXEDCASE, mnemonic_case="upper")
for curve in las.curves:
assert curve.mnemonic == curve.mnemonic.upper()
@pytest.mark.xfail(reason="not yet implemented")
def test_mnemonic_case_lower():
las = las_rs.read(_LAS_MIXEDCASE, mnemonic_case="lower")
for curve in las.curves:
assert curve.mnemonic == curve.mnemonic.lower()
@pytest.mark.xfail(reason="not yet implemented")
def test_mnemonic_case_preserve():
las = las_rs.read(_LAS_MIXEDCASE, mnemonic_case="preserve")
mnemonics = [c.mnemonic for c in las.curves]
assert "Dept" in mnemonics
assert "Gr" in mnemonics
@pytest.mark.xfail(reason="not yet implemented")
def test_ignore_header_errors_false():
with pytest.raises(las_rs.LASHeaderError):
las_rs.read(_LAS_DODGY_HEADER)
@pytest.mark.xfail(reason="not yet implemented")
def test_ignore_header_errors_true():
las = las_rs.read(_LAS_DODGY_HEADER, ignore_header_errors=True)
param_mnemonics = [p.mnemonic for p in las.params]
assert "BHT" in param_mnemonics
@pytest.mark.xfail(reason="not yet implemented")
def test_ignore_comments_default():
las = las_rs.read(_LAS_DATA_COMMENTS)
assert las.data.shape[0] == 3
assert las.curves["GR"].data[0] == pytest.approx(30.0)
@pytest.mark.xfail(reason="not yet implemented")
def test_ignore_comments_custom():
las = las_rs.read(_LAS_QUOTE_COMMENTS, ignore_comments=("#", '"'))
assert las.data.shape[0] == 2
assert las.curves["GR"].data[0] == pytest.approx(22.0)
@pytest.mark.xfail(reason="not yet implemented")
def test_read_policy_default():
las = las_rs.read(_LAS_COMMA_DECIMAL)
dept = las.curves["DEPT"].data
assert dept[0] == pytest.approx(1671.0)
@pytest.mark.xfail(reason="not yet implemented")
def test_read_policy_empty():
las = las_rs.read(_LAS_COMMA_DECIMAL, read_policy=())
dept = las.curves["DEPT"].data
assert np.isnan(dept[0]) or dept[0] != pytest.approx(1671.0)
@pytest.mark.xfail(reason="not yet implemented")
def test_null_policy_strict():
las_text = """\
~VERSION INFORMATION
VERS. 2.0 : LAS VERSION 2.0
WRAP. NO : ONE LINE PER DEPTH STEP
~WELL INFORMATION
STRT.M 10.0 : START
STOP.M 12.0 : STOP
STEP.M 1.0 : STEP
NULL. -999.25 : NULL
WELL. STRICT-1 : WELL
~CURVE INFORMATION
DEPT.M : DEPTH
GR .GAPI : GAMMA RAY
~ASCII LOG DATA
10.0 -999.25
11.0 9999.0
12.0 42.15
"""
las = las_rs.read(las_text, null_policy="strict")
gr = las.curves["GR"].data
assert np.isnan(gr[0]) assert not np.isnan(gr[1]) assert gr[1] == pytest.approx(9999.0)
@pytest.mark.xfail(reason="not yet implemented")
def test_null_policy_none():
las_text = """\
~VERSION INFORMATION
VERS. 2.0 : LAS VERSION 2.0
WRAP. NO : ONE LINE PER DEPTH STEP
~WELL INFORMATION
STRT.M 20.0 : START
STOP.M 21.0 : STOP
STEP.M 1.0 : STEP
NULL. -999.25 : NULL
WELL. NONE-1 : WELL
~CURVE INFORMATION
DEPT.M : DEPTH
GR .GAPI : GAMMA RAY
~ASCII LOG DATA
20.0 -999.25
21.0 38.5
"""
las = las_rs.read(las_text, null_policy="none")
gr = las.curves["GR"].data
assert not np.isnan(gr[0])
assert gr[0] == pytest.approx(-999.25)
@pytest.mark.xfail(reason="not yet implemented")
def test_accept_regexp_sub_recommendations_true():
las_text = """\
~VERSION INFORMATION
VERS. 2.0 : LAS VERSION 2.0
WRAP. NO : ONE LINE PER DEPTH STEP
~WELL INFORMATION
STRT.M 1.0 : START
STOP.M 2.0 : STOP
STEP.M 1.0 : STEP
NULL. -999.25 : NULL
WELL. HYPH-1 : WELL
~CURVE INFORMATION
DEPT.M : DEPTH
GR .GAPI : GAMMA RAY
~ASCII LOG DATA
1.0 50-60
2.0 65.0
"""
las = las_rs.read(las_text, accept_regexp_sub_recommendations=True)
assert isinstance(las, las_rs.LASFile)
@pytest.mark.xfail(reason="not yet implemented")
def test_index_unit_override():
las_text = """\
~VERSION INFORMATION
VERS. 2.0 : LAS VERSION 2.0
WRAP. NO : ONE LINE PER DEPTH STEP
~WELL INFORMATION
STRT.FT 100.0 : START
STOP.FT 102.0 : STOP
STEP.FT 1.0 : STEP
NULL. -999.25 : NULL
WELL. UNIT-WELL : WELL
~CURVE INFORMATION
DEPT.FT : DEPTH
GR .GAPI : GAMMA RAY
~ASCII LOG DATA
100.0 30.0
101.0 35.0
102.0 40.0
"""
las = las_rs.read(las_text, index_unit="m")
dept_unit = las.curves["DEPT"].unit
strt_unit = las.well["STRT"].unit
assert dept_unit.lower() == "m" or strt_unit.lower() == "m"
@pytest.mark.xfail(reason="not yet implemented")
def test_dtypes_auto():
las = las_rs.read(_LAS_BASIC, dtypes="auto")
dept = las.curves["DEPT"].data
gr = las.curves["GR"].data
assert dept.dtype.kind == "f"
assert gr.dtype.kind == "f"
@pytest.mark.xfail(reason="not yet implemented")
def test_dtypes_dict():
las = las_rs.read(_LAS_BASIC, dtypes={"GR": int})
dept = las.curves["DEPT"].data
gr = las.curves["GR"].data
assert dept.dtype.kind == "f"
assert gr.dtype.kind == "i"
assert gr[0] == 44
@pytest.mark.xfail(reason="not yet implemented")
def test_dtypes_list():
las = las_rs.read(_LAS_BASIC, dtypes=[float, float])
for curve in las.curves:
assert curve.data.dtype.kind == "f"
@pytest.mark.xfail(reason="not yet implemented")
def test_dtypes_false():
las = las_rs.read(_LAS_BASIC, dtypes=False)
dept = las.curves["DEPT"].data
assert dept.dtype.kind in ("U", "S", "O")
assert "200" in str(dept[0])
@pytest.mark.xfail(reason="not yet implemented")
def test_encoding_explicit():
las = las_rs.read(_LAS_BASIC, encoding="utf-8")
assert isinstance(las, las_rs.LASFile)
assert las.curves["GR"].data[0] == pytest.approx(44.10)
@pytest.mark.xfail(reason="not yet implemented")
def test_encoding_errors_replace():
latin1_path = fixture("encodings", "latin1.las")
las = las_rs.read(latin1_path, encoding="utf-8", encoding_errors="replace")
assert isinstance(las, las_rs.LASFile)
@pytest.mark.xfail(reason="not yet implemented")
def test_autodetect_encoding_true():
latin1_path = fixture("encodings", "latin1.las")
las = las_rs.read(latin1_path, autodetect_encoding=True)
assert isinstance(las, las_rs.LASFile)
assert len(las.curves) > 0
@pytest.mark.xfail(reason="not yet implemented")
def test_autodetect_encoding_false():
las = las_rs.read(_LAS_BASIC, autodetect_encoding=False)
assert isinstance(las, las_rs.LASFile)
assert las.curves["GR"].data[2] == pytest.approx(52.80)
@pytest.mark.xfail(reason="not yet implemented")
def test_ignore_data_comments():
las = las_rs.read(_LAS_DATA_COMMENTS, ignore_data_comments="#")
assert las.data.shape[0] == 3
gr = las.curves["GR"].data
assert gr[0] == pytest.approx(30.0)
assert gr[1] == pytest.approx(35.0)
assert gr[2] == pytest.approx(40.0)