las-read-rs 0.1.1__cp310-cp310-manylinux_2_34_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: las_read_rs
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: A high-performance LAS (Log ASCII Standard) file reader written in Rust
|
|
5
|
+
Author-email: Emiliano Flores <jemilianofl@github.com>
|
|
6
|
+
Requires-Python: >=3.7
|
|
7
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
8
|
+
|
|
9
|
+
# LAS_read_rs
|
|
10
|
+
|
|
11
|
+
**LAS_read_rs** is a high-performance Python library for reading **LAS (Log ASCII Standard)** files, written in **Rust** using `pyo3` and `nom`. It is designed to be a faster alternative to pure Python implementations, capable of parsing large geophysical log files efficiently.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- ๐ **High Performance**: Built with Rust's `nom` parser combinators and parallelized with `rayon` for maximum speed.
|
|
16
|
+
- ๐งต **Multi-threaded**: parses the data section (`~ASCII`) in parallel chunks.
|
|
17
|
+
- ๐ฆ **Easy Installation**: Distributed as a standard Python wheel via PyPI.
|
|
18
|
+
- ๐ **Pythonic Interface**: Simple `read()` function returning a `LASFile` object.
|
|
19
|
+
- โ
**Standard Compliant**: Supports LAS 2.0 (and 1.2 compatible structure).
|
|
20
|
+
|
|
21
|
+
## Benchmarks
|
|
22
|
+
|
|
23
|
+
Parsed a LAS file with **100,000 lines** of data:
|
|
24
|
+
- **LAS_read_rs**: ~0.8s (Pure parsing time) / ~3.8s (Including CLI overhead benchmarks)
|
|
25
|
+
- **Pure Python**: Significantly slower (typically 10x-50x slower depending on implementation).
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install LAS_read_rs
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Usage
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import lasio_rs
|
|
37
|
+
|
|
38
|
+
# Read a LAS file
|
|
39
|
+
las = lasio_rs.read("path/to/well_log.las")
|
|
40
|
+
|
|
41
|
+
# Access Version Information
|
|
42
|
+
print(las.version)
|
|
43
|
+
# Output (JSON representation):
|
|
44
|
+
# {
|
|
45
|
+
# "VERS": {"mnemonic": "VERS", "value": "2.0", "descr": "CWLS LOG ASCII STANDARD - VERSION 2.0"},
|
|
46
|
+
# "WRAP": {"mnemonic": "WRAP", "value": "NO", "descr": "One line per depth step"}
|
|
47
|
+
# }
|
|
48
|
+
|
|
49
|
+
# Access Well Metadata (e.g., STRT, STOP, NULL)
|
|
50
|
+
# Note: Currently exposed mainly via debug getters or verifying structure
|
|
51
|
+
# Future versions will expose a full dictionary-like interface.
|
|
52
|
+
|
|
53
|
+
print(f"Well Name: {las.well_name}")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Structure
|
|
57
|
+
|
|
58
|
+
The library maps LAS sections to Rust structs:
|
|
59
|
+
- `~Version` -> `las.version`
|
|
60
|
+
- `~Well` -> `las.well`
|
|
61
|
+
- `~Curves` -> `las.curves` (Metadata)
|
|
62
|
+
- `~ASCII` -> Data columns (Stored internally as efficient vectors)
|
|
63
|
+
|
|
64
|
+
## Building from Source
|
|
65
|
+
|
|
66
|
+
Requirements:
|
|
67
|
+
- Python 3.7+
|
|
68
|
+
- Rust (cargo, rustc)
|
|
69
|
+
- `maturin`
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install maturin
|
|
73
|
+
maturin develop --release
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT
|
|
79
|
+
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
las_read_rs-0.1.1.dist-info/METADATA,sha256=VOYbTSMyNgWnAxv6vHvFTg3TR8oAA_eu3q4KJxbg4Q0,2333
|
|
2
|
+
las_read_rs-0.1.1.dist-info/WHEEL,sha256=wWeP6JRxAL_oJlqLsckA512CC09LPG7OAdt_mycafo4,109
|
|
3
|
+
lasio_rs/__init__.py,sha256=EgGxrSR3njk8PPL7ev0prizObOipRyfBui-QVI7dkjQ,31
|
|
4
|
+
lasio_rs/_lasio_rs.cp313-win_amd64.pyd,sha256=sE_eS52Q2ayVUdKwPSgzQQxzUy2WN1NnVtaVASjKc_8,1380352
|
|
5
|
+
lasio_rs/_lasio_rs.cpython-310-x86_64-linux-gnu.so,sha256=yvH5W_qywWqcrNws3lnjEuCTnr8fGZRVLoZqJz8gQbw,928496
|
|
6
|
+
lasio_rs/las.py,sha256=EKQzfFfnXo_RyY3Ip6h0eQQvkjjyh5_y6yEizaE6Hys,4382
|
|
7
|
+
las_read_rs-0.1.1.dist-info/RECORD,,
|
lasio_rs/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .las import read, LASFile
|
|
Binary file
|
|
Binary file
|
lasio_rs/las.py
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
from ._lasio_rs import read as _rust_read
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SectionItems:
|
|
6
|
+
def __init__(self, data_dict):
|
|
7
|
+
# Allow init from dict of HeaderItems or dict of dicts (from json)
|
|
8
|
+
self._data = {}
|
|
9
|
+
for k, v in data_dict.items():
|
|
10
|
+
if isinstance(v, dict):
|
|
11
|
+
# Check if it is a Curve dict or Header dict
|
|
12
|
+
self._data[k] = HeaderItem(v)
|
|
13
|
+
else:
|
|
14
|
+
self._data[k] = v
|
|
15
|
+
|
|
16
|
+
def __getitem__(self, key):
|
|
17
|
+
item = self._data.get(key)
|
|
18
|
+
if item:
|
|
19
|
+
return item
|
|
20
|
+
raise KeyError(f"{key} not found")
|
|
21
|
+
|
|
22
|
+
def keys(self):
|
|
23
|
+
return self._data.keys()
|
|
24
|
+
|
|
25
|
+
def values(self):
|
|
26
|
+
return self._data.values()
|
|
27
|
+
|
|
28
|
+
def items(self):
|
|
29
|
+
return self._data.items()
|
|
30
|
+
|
|
31
|
+
def get(self, key, default=None):
|
|
32
|
+
return self._data.get(key, default)
|
|
33
|
+
|
|
34
|
+
def __repr__(self):
|
|
35
|
+
return f"SectionItems({list(self.keys())})"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class HeaderItem:
|
|
39
|
+
def __init__(self, item_dict):
|
|
40
|
+
self.mnemonic = item_dict.get("mnemonic")
|
|
41
|
+
self.unit = item_dict.get("unit")
|
|
42
|
+
self.value = item_dict.get("value")
|
|
43
|
+
self.descr = item_dict.get("descr")
|
|
44
|
+
self.original_mnemonic = self.mnemonic # Compatibility
|
|
45
|
+
|
|
46
|
+
def __repr__(self):
|
|
47
|
+
return f'HeaderItem(mnemonic="{self.mnemonic}", unit="{self.unit}", value="{self.value}", descr="{self.descr}")'
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class CurveItem(HeaderItem):
|
|
51
|
+
def __init__(self, item_dict, las_file_ref):
|
|
52
|
+
super().__init__(item_dict)
|
|
53
|
+
self._las = las_file_ref
|
|
54
|
+
self._data = None
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def data(self):
|
|
58
|
+
if self._data is None:
|
|
59
|
+
# Lazy load from Rust
|
|
60
|
+
self._data = self._las._rust.get_curve_data(self.mnemonic)
|
|
61
|
+
if self._data is None:
|
|
62
|
+
self._data = []
|
|
63
|
+
return self._data
|
|
64
|
+
|
|
65
|
+
def __getitem__(self, index):
|
|
66
|
+
return self.data[index]
|
|
67
|
+
|
|
68
|
+
def __len__(self):
|
|
69
|
+
return len(self.data)
|
|
70
|
+
|
|
71
|
+
def __repr__(self):
|
|
72
|
+
return f'CurveItem(mnemonic="{self.mnemonic}", unit="{self.unit}", value="{self.value}", descr="{self.descr}", data.shape=({len(self)},))'
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class LASFile:
|
|
76
|
+
def __init__(self, rust_las):
|
|
77
|
+
self._rust = rust_las
|
|
78
|
+
|
|
79
|
+
# Hydrate metadata from JSON
|
|
80
|
+
header_json = rust_las.json_headers
|
|
81
|
+
header_data = json.loads(header_json)
|
|
82
|
+
|
|
83
|
+
self.version = SectionItems(header_data.get("version", {}))
|
|
84
|
+
self.well = SectionItems(header_data.get("well", {}))
|
|
85
|
+
self.params = SectionItems(header_data.get("params", {}))
|
|
86
|
+
|
|
87
|
+
# Curves needs special handling to link back to Rust for data
|
|
88
|
+
raw_curves = header_data.get("curves", {})
|
|
89
|
+
self.curves = SectionItems({})
|
|
90
|
+
# Overwrite with CurveItems
|
|
91
|
+
curve_dict = {}
|
|
92
|
+
for k, v in raw_curves.items():
|
|
93
|
+
curve_dict[k] = CurveItem(v, self)
|
|
94
|
+
self.curves = SectionItems(curve_dict)
|
|
95
|
+
|
|
96
|
+
def __getitem__(self, key):
|
|
97
|
+
# Access curve data directly by mnemonic
|
|
98
|
+
if isinstance(key, str):
|
|
99
|
+
if key in self.curves.keys():
|
|
100
|
+
return self.curves[key].data
|
|
101
|
+
raise KeyError(f"Curve {key} not found")
|
|
102
|
+
|
|
103
|
+
def keys(self):
|
|
104
|
+
return self.curves.keys()
|
|
105
|
+
|
|
106
|
+
def to_df(self):
|
|
107
|
+
"""Convert LAS data to a pandas DataFrame.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
pandas.DataFrame: DataFrame with curve mnemonics as columns.
|
|
111
|
+
"""
|
|
112
|
+
try:
|
|
113
|
+
import pandas as pd
|
|
114
|
+
except ImportError:
|
|
115
|
+
raise ImportError(
|
|
116
|
+
"pandas is required for to_df(). Install with: pip install pandas"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
data = {}
|
|
120
|
+
for mnemonic in self.curves.keys():
|
|
121
|
+
curve = self.curves[mnemonic]
|
|
122
|
+
data[mnemonic] = curve.data
|
|
123
|
+
return pd.DataFrame(data)
|
|
124
|
+
|
|
125
|
+
def to_polars(self):
|
|
126
|
+
"""Convert LAS data to a polars DataFrame.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
polars.DataFrame: DataFrame with curve mnemonics as columns.
|
|
130
|
+
"""
|
|
131
|
+
try:
|
|
132
|
+
import polars as pl
|
|
133
|
+
except ImportError:
|
|
134
|
+
raise ImportError(
|
|
135
|
+
"polars is required for to_polars(). Install with: pip install polars"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
data = {}
|
|
139
|
+
for mnemonic in self.curves.keys():
|
|
140
|
+
curve = self.curves[mnemonic]
|
|
141
|
+
data[mnemonic] = curve.data
|
|
142
|
+
return pl.DataFrame(data)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def read(file_path):
|
|
146
|
+
rust_obj = _rust_read(str(file_path))
|
|
147
|
+
return LASFile(rust_obj)
|