lasmnemonicsid 0.0.2__py3-none-any.whl → 0.0.3rc0__py3-none-any.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.
- LASMnemonicsID/LAS/LAS.py +93 -47
- LASMnemonicsID/LAS/__init__.py +5 -4
- {lasmnemonicsid-0.0.2.dist-info → lasmnemonicsid-0.0.3rc0.dist-info}/METADATA +38 -11
- lasmnemonicsid-0.0.3rc0.dist-info/RECORD +11 -0
- {lasmnemonicsid-0.0.2.dist-info → lasmnemonicsid-0.0.3rc0.dist-info}/WHEEL +1 -1
- lasmnemonicsid-0.0.2.dist-info/RECORD +0 -11
- {lasmnemonicsid-0.0.2.dist-info → lasmnemonicsid-0.0.3rc0.dist-info}/licenses/LICENSE +0 -0
- {lasmnemonicsid-0.0.2.dist-info → lasmnemonicsid-0.0.3rc0.dist-info}/top_level.txt +0 -0
LASMnemonicsID/LAS/LAS.py
CHANGED
|
@@ -20,11 +20,7 @@ from os.path import join
|
|
|
20
20
|
from sys import stdout
|
|
21
21
|
from pathlib import Path
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# Function that create the mnemonic dictionary
|
|
23
|
+
# Function that creates the mnemonic dictionary
|
|
28
24
|
def create_mnemonic_dict(
|
|
29
25
|
gamma_names,
|
|
30
26
|
sp_names,
|
|
@@ -39,9 +35,8 @@ def create_mnemonic_dict(
|
|
|
39
35
|
pe_names,
|
|
40
36
|
):
|
|
41
37
|
"""
|
|
42
|
-
Function that
|
|
38
|
+
Function that creates the mnemonic dictionary with the mnemonics per log type.
|
|
43
39
|
"""
|
|
44
|
-
|
|
45
40
|
mnemonic_dict = {
|
|
46
41
|
"gamma": gamma_names,
|
|
47
42
|
"sp": sp_names,
|
|
@@ -57,63 +52,82 @@ def create_mnemonic_dict(
|
|
|
57
52
|
}
|
|
58
53
|
return mnemonic_dict
|
|
59
54
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def parseLAS(directory_path, verbose=True):
|
|
55
|
+
def parseLAS(input_path, verbose=True, preferred_names=None):
|
|
63
56
|
"""
|
|
64
|
-
Parse
|
|
57
|
+
Parse LAS file or all in directory → DataFrame or {filename: df}.
|
|
65
58
|
|
|
66
59
|
Args:
|
|
67
|
-
|
|
68
|
-
verbose (bool): Print
|
|
60
|
+
input_path (str/Path): LAS file or directory
|
|
61
|
+
verbose (bool): Print info
|
|
62
|
+
preferred_names (dict, optional): Mapping of curve types to preferred column names and preferred original columns.
|
|
63
|
+
Example: {"deepres": "RT", "deepres_preferred_original": "AT90", "gamma": "GR"}
|
|
64
|
+
If not provided, defaults to standard petrophysical names.
|
|
69
65
|
|
|
70
66
|
Returns:
|
|
71
|
-
|
|
67
|
+
DataFrame (single) or dict {filename: df} (multiple/dir)
|
|
72
68
|
"""
|
|
73
|
-
|
|
74
|
-
well_logs = {}
|
|
69
|
+
input_path = Path(input_path)
|
|
75
70
|
|
|
76
|
-
#
|
|
77
|
-
|
|
71
|
+
# Define default standard names
|
|
72
|
+
std_names = {
|
|
73
|
+
"gamma": "GR",
|
|
74
|
+
"sp": "SP",
|
|
75
|
+
"caliper": "CALI",
|
|
76
|
+
"deepres": "RT",
|
|
77
|
+
"rxo": "RXO",
|
|
78
|
+
"density": "RHOB",
|
|
79
|
+
"density_correction": "DRHO",
|
|
80
|
+
"neutron": "NPHI",
|
|
81
|
+
"dtc": "DT",
|
|
82
|
+
"dts": "DTS",
|
|
83
|
+
"pe": "PEF"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Update with user preferences if provided
|
|
87
|
+
if preferred_names:
|
|
88
|
+
std_names.update(preferred_names)
|
|
89
|
+
|
|
90
|
+
# Case 1: Single File
|
|
91
|
+
if input_path.is_file() and input_path.suffix.lower() == '.las':
|
|
92
|
+
df = _read_single_las(input_path, verbose, std_names)
|
|
93
|
+
return df if df is not None else None
|
|
78
94
|
|
|
95
|
+
# Case 2: Directory (Recursive)
|
|
96
|
+
las_files = list(input_path.rglob("*.las"))
|
|
79
97
|
if not las_files:
|
|
80
98
|
if verbose:
|
|
81
|
-
print("No LAS files found
|
|
99
|
+
print(f"No LAS files found in {input_path}")
|
|
82
100
|
return {}
|
|
83
101
|
|
|
84
|
-
|
|
85
|
-
# Return single DataFrame if only one file
|
|
86
|
-
return _read_single_las(las_files[0], verbose)
|
|
87
|
-
|
|
88
|
-
# Multiple files: group by parent folder
|
|
102
|
+
las_dict = {}
|
|
89
103
|
for las_file in las_files:
|
|
90
|
-
|
|
91
|
-
if folder_name not in well_logs:
|
|
92
|
-
well_logs[folder_name] = {}
|
|
93
|
-
|
|
94
|
-
df = _read_single_las(las_file, verbose)
|
|
104
|
+
df = _read_single_las(las_file, verbose, std_names)
|
|
95
105
|
if df is not None:
|
|
96
|
-
|
|
97
|
-
|
|
106
|
+
filename = las_file.name
|
|
107
|
+
las_dict[filename] = df
|
|
98
108
|
|
|
99
|
-
|
|
100
|
-
|
|
109
|
+
# Return single DF if only 1 file found, else dict
|
|
110
|
+
if len(las_dict) == 1:
|
|
111
|
+
return next(iter(las_dict.values()))
|
|
112
|
+
|
|
113
|
+
return las_dict
|
|
101
114
|
|
|
102
|
-
def _read_single_las(las_file_path, verbose):
|
|
103
|
-
"""Read single LAS file to DataFrame"""
|
|
115
|
+
def _read_single_las(las_file_path, verbose, std_names):
|
|
116
|
+
"""Read single LAS file to DataFrame and standardize ALL curves."""
|
|
104
117
|
try:
|
|
105
118
|
las_data = lasio.read(las_file_path)
|
|
106
119
|
df = las_data.df()
|
|
120
|
+
|
|
107
121
|
if df is None or df.empty:
|
|
108
122
|
if verbose:
|
|
109
123
|
print(f"✗ Empty DataFrame: {las_file_path.name}")
|
|
110
124
|
return None
|
|
111
125
|
|
|
126
|
+
# Ensure index is depth (float)
|
|
112
127
|
df.index = df.index.astype(float)
|
|
113
|
-
# df.dropna(inplace=True)
|
|
114
128
|
|
|
115
|
-
# Standardize GR
|
|
116
|
-
|
|
129
|
+
# Standardize ALL curves (GR, RHOB, NPHI, etc.)
|
|
130
|
+
_standardize_all_curves(las_data, df, std_names)
|
|
117
131
|
|
|
118
132
|
if verbose:
|
|
119
133
|
print(f"✓ {las_file_path.name}")
|
|
@@ -127,7 +141,6 @@ def _read_single_las(las_file_path, verbose):
|
|
|
127
141
|
print(f"✗ Error in {las_file_path.name}: {type(e).__name__}: {e}")
|
|
128
142
|
return None
|
|
129
143
|
|
|
130
|
-
|
|
131
144
|
def _get_well_name(las_file_path):
|
|
132
145
|
"""Extract well name from LAS file"""
|
|
133
146
|
try:
|
|
@@ -136,13 +149,46 @@ def _get_well_name(las_file_path):
|
|
|
136
149
|
except:
|
|
137
150
|
return las_file_path.stem
|
|
138
151
|
|
|
152
|
+
def _standardize_all_curves(las_data, df, std_names):
|
|
153
|
+
"""
|
|
154
|
+
Rename ALL curves in the DataFrame to standard abbreviations
|
|
155
|
+
based on the mnemonic dictionary.
|
|
156
|
+
"""
|
|
157
|
+
# 1. Get the dictionary of aliases
|
|
158
|
+
mnem_dict = create_mnemonic_dict(
|
|
159
|
+
gamma_names, sp_names, caliper_names, deepres_names, rxo_names,
|
|
160
|
+
density_names, density_correction_names, neutron_names,
|
|
161
|
+
dtc_names, dts_names, pe_names
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# 2. Track which columns we've already renamed to avoid duplicates
|
|
165
|
+
renamed = set()
|
|
139
166
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
167
|
+
# 3. For each curve type, find all aliases in the file
|
|
168
|
+
for curve_type, aliases in mnem_dict.items():
|
|
169
|
+
# Find all matching columns in df
|
|
170
|
+
matching = [col for col in df.columns if col.lower() in [a.lower() for a in aliases]]
|
|
171
|
+
|
|
172
|
+
if not matching:
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
# Use standard name if provided, otherwise use curve_type.upper()
|
|
176
|
+
target_name = std_names.get(curve_type, curve_type.upper())
|
|
177
|
+
|
|
178
|
+
# If a preferred original column is specified, use it
|
|
179
|
+
preferred_original = std_names.get(f"{curve_type}_preferred_original")
|
|
180
|
+
|
|
181
|
+
if preferred_original and preferred_original in matching:
|
|
182
|
+
# Rename preferred original to target_name
|
|
183
|
+
df.rename(columns={preferred_original: target_name}, inplace=True)
|
|
184
|
+
renamed.add(target_name)
|
|
185
|
+
else:
|
|
186
|
+
# Otherwise, pick the first matching alias
|
|
187
|
+
df.rename(columns={matching[0]: target_name}, inplace=True)
|
|
188
|
+
renamed.add(target_name)
|
|
189
|
+
|
|
190
|
+
# Remove all other matching columns
|
|
191
|
+
for col in matching:
|
|
192
|
+
if col != target_name and col in df.columns:
|
|
193
|
+
df.drop(columns=[col], inplace=True)
|
|
148
194
|
|
LASMnemonicsID/LAS/__init__.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from .LAS import (
|
|
2
2
|
parseLAS,
|
|
3
3
|
create_mnemonic_dict,
|
|
4
|
-
_read_single_las,
|
|
5
4
|
_get_well_name,
|
|
6
|
-
|
|
5
|
+
_read_single_las # Keep helpers if needed
|
|
7
6
|
)
|
|
8
7
|
|
|
9
8
|
__all__ = [
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
"parseLAS",
|
|
10
|
+
"create_mnemonic_dict",
|
|
11
|
+
"_get_well_name",
|
|
12
|
+
"_read_single_las"
|
|
12
13
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lasmnemonicsid
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.3rc0
|
|
4
4
|
Summary: Well log mnemonic identification using lasio and dlisio to load LAS/DLIS files into DataFrames
|
|
5
5
|
Author-email: Nobleza Energy <info@nobleza-energy.com>
|
|
6
6
|
License: MIT
|
|
@@ -36,7 +36,6 @@ Requires-Dist: flake8>=6.0.0; extra == "dev"
|
|
|
36
36
|
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
37
37
|
Dynamic: license-file
|
|
38
38
|
|
|
39
|
-
|
|
40
39
|
<p align="center">
|
|
41
40
|
<img src="https://raw.githubusercontent.com/Nobleza-Energy/LASMnemonicsID/main/logo.png" alt="LASMnemonicsID Logo" width="200"/>
|
|
42
41
|
</p>
|
|
@@ -61,10 +60,9 @@ Dynamic: license-file
|
|
|
61
60
|
pip install lasmnemonicsid
|
|
62
61
|
```
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
63
|
## 🚀 QuickStart
|
|
67
|
-
|
|
64
|
+
|
|
65
|
+
```python
|
|
68
66
|
from LASMnemonicsID.LAS import parseLAS
|
|
69
67
|
|
|
70
68
|
# Load LAS file
|
|
@@ -72,12 +70,41 @@ df = parseLAS("your_well.las")
|
|
|
72
70
|
print(df.head())
|
|
73
71
|
```
|
|
74
72
|
|
|
75
|
-
## 🧪 Test with your Data
|
|
76
|
-
|
|
73
|
+
## 🧪 Test with your Data: Multiple files will load into a dictionary
|
|
74
|
+
|
|
75
|
+
```python
|
|
77
76
|
from LASMnemonicsID.LAS import parseLAS
|
|
78
77
|
|
|
79
|
-
# Load
|
|
80
|
-
|
|
81
|
-
print(
|
|
82
|
-
|
|
78
|
+
# Load all .las within the Directory → {filename: df}
|
|
79
|
+
data = parseLAS("/path/to/your/data/")
|
|
80
|
+
print("Files:", list(data.keys()))
|
|
81
|
+
|
|
82
|
+
# Dataframes
|
|
83
|
+
df = parseLAS('/path/to/yourfile.las')
|
|
84
|
+
print(df.head())
|
|
83
85
|
```
|
|
86
|
+
|
|
87
|
+
## 📈 Star History
|
|
88
|
+
|
|
89
|
+
[](https://star-history.com/#Nobleza-Energy/LASMnemonicsID&Date)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
## 📄 How to Cite
|
|
93
|
+
|
|
94
|
+
If you use `LASMnemonicsID` in your research or project, please cite it as follows:
|
|
95
|
+
|
|
96
|
+
**APA**
|
|
97
|
+
|
|
98
|
+
> Nobleza Energy. (2025). LASMnemonicsID: Well log mnemonic identification using lasio and dlisio [Software]. GitHub. https://github.com/Nobleza-Energy/LASMnemonicsID
|
|
99
|
+
|
|
100
|
+
**BibTeX**
|
|
101
|
+
|
|
102
|
+
```bibtex
|
|
103
|
+
@software{LASMnemonicsID,
|
|
104
|
+
author = {Nobleza Energy},
|
|
105
|
+
title = {LASMnemonicsID: Well log mnemonic identification using lasio and dlisio},
|
|
106
|
+
year = {2025},
|
|
107
|
+
publisher = {GitHub},
|
|
108
|
+
journal = {GitHub repository},
|
|
109
|
+
url = {https://github.com/Nobleza-Energy/LASMnemonicsID}
|
|
110
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
LASMnemonicsID/__init__.py,sha256=IjJHoiHWr1CfP3K01xW61UhnJYP_9LOOaCqJnhLFlPc,309
|
|
2
|
+
LASMnemonicsID/DLIS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
LASMnemonicsID/LAS/LAS.py,sha256=gxeLlARZJV3ECxIQaoqO8YeOUfnlMUBKXqRFY-JivCs,6048
|
|
4
|
+
LASMnemonicsID/LAS/__init__.py,sha256=dTM87nn0zNUaKp29HocOODJT_-VM1CZED9Ar_FSOr-4,232
|
|
5
|
+
LASMnemonicsID/utils/__init__.py,sha256=ree81DUTsdjXfO3h-q7YyNrV6mTIKSGxgWPWGGTSVU0,1388
|
|
6
|
+
LASMnemonicsID/utils/mnemonics.py,sha256=VU25CXmQvUo0sS3Y6kG_G7KwRE2CiuoJeC7LT6FmNzg,7283
|
|
7
|
+
lasmnemonicsid-0.0.3rc0.dist-info/licenses/LICENSE,sha256=6r9JOUiNw1exfcc0jlOi50fDStidfqyQ2PAYQh4lzEQ,1071
|
|
8
|
+
lasmnemonicsid-0.0.3rc0.dist-info/METADATA,sha256=AXUCpIS5uLGwI6yGlBIctpyI0EN-KZGo5u6cimjOi_E,3706
|
|
9
|
+
lasmnemonicsid-0.0.3rc0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
10
|
+
lasmnemonicsid-0.0.3rc0.dist-info/top_level.txt,sha256=bdt6EHMrwbzFA9jA_xbTqRrOV6T4zDs3QojjEz8HSBk,15
|
|
11
|
+
lasmnemonicsid-0.0.3rc0.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
LASMnemonicsID/__init__.py,sha256=IjJHoiHWr1CfP3K01xW61UhnJYP_9LOOaCqJnhLFlPc,309
|
|
2
|
-
LASMnemonicsID/DLIS/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
LASMnemonicsID/LAS/LAS.py,sha256=7WRetjZG9-M39KNxpUisAkbh8mv60BWW-uTEUkwvkF8,3849
|
|
4
|
-
LASMnemonicsID/LAS/__init__.py,sha256=4cNesZ581Cl_PN_c4DHTGfkc5HiKtnbhP81vJ6-3nPM,187
|
|
5
|
-
LASMnemonicsID/utils/__init__.py,sha256=ree81DUTsdjXfO3h-q7YyNrV6mTIKSGxgWPWGGTSVU0,1388
|
|
6
|
-
LASMnemonicsID/utils/mnemonics.py,sha256=VU25CXmQvUo0sS3Y6kG_G7KwRE2CiuoJeC7LT6FmNzg,7283
|
|
7
|
-
lasmnemonicsid-0.0.2.dist-info/licenses/LICENSE,sha256=6r9JOUiNw1exfcc0jlOi50fDStidfqyQ2PAYQh4lzEQ,1071
|
|
8
|
-
lasmnemonicsid-0.0.2.dist-info/METADATA,sha256=9XdXauxiGe80GgPo9GWFrbAjSGxoyOibBPmGPmJ-WLk,2811
|
|
9
|
-
lasmnemonicsid-0.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
10
|
-
lasmnemonicsid-0.0.2.dist-info/top_level.txt,sha256=bdt6EHMrwbzFA9jA_xbTqRrOV6T4zDs3QojjEz8HSBk,15
|
|
11
|
-
lasmnemonicsid-0.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|