well-log-toolkit 0.1.122__py3-none-any.whl → 0.1.124__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.
@@ -1507,7 +1507,10 @@ class WellDataManager:
1507
1507
  def load_las(
1508
1508
  self,
1509
1509
  filepath: Union[str, Path, list[Union[str, Path]]],
1510
- sampled: bool = False
1510
+ path: Optional[Union[str, Path]] = None,
1511
+ sampled: bool = False,
1512
+ combine: Optional[str] = None,
1513
+ source_name: Optional[str] = None
1511
1514
  ) -> 'WellDataManager':
1512
1515
  """
1513
1516
  Load LAS file(s), auto-create well if needed.
@@ -1515,10 +1518,26 @@ class WellDataManager:
1515
1518
  Parameters
1516
1519
  ----------
1517
1520
  filepath : Union[str, Path, list[Union[str, Path]]]
1518
- Path to LAS file or list of paths to LAS files
1521
+ Path to LAS file or list of paths to LAS files.
1522
+ When providing a list, filenames can be relative to the path parameter.
1523
+ path : Union[str, Path], optional
1524
+ Directory path to prepend to all filenames. Useful when loading multiple
1525
+ files from the same directory. If None, filenames are used as-is.
1519
1526
  sampled : bool, default False
1520
1527
  If True, mark all properties from the LAS file(s) as 'sampled' type.
1521
1528
  Use this for core plug data or other point measurements.
1529
+ combine : str, optional
1530
+ When loading multiple files, combine files from the same well into a single source:
1531
+ - None (default): Load files as separate sources, no combining
1532
+ - 'match': Combine using match method (safest, errors on mismatch)
1533
+ - 'resample': Combine using resample method (interpolates to first file)
1534
+ - 'concat': Combine using concat method (merges all unique depths)
1535
+ Files are automatically grouped by well name. If 4 files from 2 wells are loaded,
1536
+ 2 combined sources are created (one per well).
1537
+ source_name : str, optional
1538
+ Name for combined source when combine is specified. If not specified,
1539
+ uses 'combined_match', 'combined_resample', or 'combined_concat'.
1540
+ When files span multiple wells, the well name is prepended automatically.
1522
1541
 
1523
1542
  Returns
1524
1543
  -------
@@ -1535,26 +1554,134 @@ class WellDataManager:
1535
1554
  >>> manager = WellDataManager()
1536
1555
  >>> manager.load_las("well1.las")
1537
1556
  >>> manager.load_las(["well2.las", "well3.las"])
1557
+
1538
1558
  >>> # Load core plug data
1539
1559
  >>> manager.load_las("core_data.las", sampled=True)
1540
- >>> well = manager.well_12_3_2_B
1560
+
1561
+ >>> # Load multiple files from same directory
1562
+ >>> manager.load_las(
1563
+ ... ["file1.las", "file2.las", "file3.las"],
1564
+ ... path="data/well_logs"
1565
+ ... )
1566
+
1567
+ >>> # Load and combine files (automatically groups by well)
1568
+ >>> manager.load_las(
1569
+ ... ["36_7-5_B_CorePerm.las", "36_7-5_B_CorePor.las",
1570
+ ... "36_7-4_CorePerm.las", "36_7-4_CorePor.las"],
1571
+ ... path="data/",
1572
+ ... combine="match",
1573
+ ... source_name="CorePlugs"
1574
+ ... )
1575
+ Loaded sources:
1576
+ - Well 36/7-5 B: CorePlugs (2 files combined)
1577
+ - Well 36/7-4: CorePlugs (2 files combined)
1541
1578
  """
1542
1579
  # Handle list of files
1543
1580
  if isinstance(filepath, list):
1544
- for file in filepath:
1545
- self.load_las(file, sampled=sampled)
1581
+ # Prepend path to all filenames if provided
1582
+ if path is not None:
1583
+ base_path = Path(path)
1584
+ file_paths = [base_path / file for file in filepath]
1585
+ else:
1586
+ file_paths = filepath
1587
+
1588
+ # If combine is specified, group files by well and combine each group
1589
+ if combine is not None:
1590
+ # Group files by well name
1591
+ from collections import defaultdict
1592
+ well_groups = defaultdict(list)
1593
+
1594
+ for file_path in file_paths:
1595
+ las = LasFile(file_path)
1596
+ if las.well_name is None:
1597
+ raise LasFileError(
1598
+ f"LAS file {file_path} has no WELL name in header. "
1599
+ "Cannot determine which well to load into."
1600
+ )
1601
+ well_groups[las.well_name].append(file_path)
1602
+
1603
+ # Track loaded sources for debug output
1604
+ loaded_sources = []
1605
+
1606
+ # Process each well group
1607
+ for well_name, files_for_well in well_groups.items():
1608
+ sanitized_name = sanitize_well_name(well_name)
1609
+ well_key = f"well_{sanitized_name}"
1610
+
1611
+ # Ensure well exists
1612
+ if well_key not in self._wells:
1613
+ self._wells[well_key] = Well(
1614
+ name=well_name,
1615
+ sanitized_name=sanitized_name,
1616
+ parent_manager=self
1617
+ )
1618
+ self._name_mapping[well_name] = well_key
1619
+
1620
+ # Load files into well with combine
1621
+ self._wells[well_key].load_las(
1622
+ files_for_well,
1623
+ path=None, # Path already prepended
1624
+ sampled=sampled,
1625
+ combine=combine,
1626
+ source_name=source_name
1627
+ )
1628
+
1629
+ # Track what was loaded
1630
+ actual_source_name = source_name if source_name else f"combined_{combine}"
1631
+ loaded_sources.append(
1632
+ (well_name, actual_source_name, len(files_for_well))
1633
+ )
1634
+
1635
+ # Print debug output
1636
+ print("Loaded sources:")
1637
+ for well_name, src_name, file_count in loaded_sources:
1638
+ print(f" - Well {well_name}: {src_name} ({file_count} file{'s' if file_count > 1 else ''} combined)")
1639
+
1640
+ return self
1641
+
1642
+ # No combine - load each file separately
1643
+ loaded_sources = []
1644
+ for file_path in file_paths:
1645
+ # Read well name and source name before loading
1646
+ las = LasFile(file_path)
1647
+ if las.well_name is None:
1648
+ raise LasFileError(
1649
+ f"LAS file {file_path} has no WELL name in header. "
1650
+ "Cannot determine which well to load into."
1651
+ )
1652
+ well_name = las.well_name
1653
+ source_name_from_file = las.source_name
1654
+
1655
+ # Load the file
1656
+ self.load_las(file_path, path=None, sampled=sampled, combine=None, source_name=None)
1657
+
1658
+ # Track what was loaded
1659
+ loaded_sources.append((well_name, source_name_from_file))
1660
+
1661
+ # Print debug output
1662
+ print("Loaded sources:")
1663
+ for well_name, src_name in loaded_sources:
1664
+ print(f" - Well {well_name}: {src_name}")
1665
+
1546
1666
  return self
1547
1667
 
1548
1668
  # Handle single file
1549
- las = LasFile(filepath)
1669
+ # Prepend path if provided
1670
+ if path is not None:
1671
+ file_path = Path(path) / filepath
1672
+ else:
1673
+ file_path = filepath
1674
+
1675
+ las = LasFile(file_path)
1550
1676
  well_name = las.well_name
1551
1677
 
1552
1678
  if well_name is None:
1553
1679
  raise LasFileError(
1554
- f"LAS file {filepath} has no WELL name in header. "
1680
+ f"LAS file {file_path} has no WELL name in header. "
1555
1681
  "Cannot determine which well to load into."
1556
1682
  )
1557
1683
 
1684
+ source_name_from_file = las.source_name
1558
1685
  sanitized_name = sanitize_well_name(well_name)
1559
1686
  # Use well_ prefix for dictionary key (attribute access)
1560
1687
  well_key = f"well_{sanitized_name}"
@@ -1571,6 +1698,10 @@ class WellDataManager:
1571
1698
  # Load into well
1572
1699
  self._wells[well_key].load_las(las, sampled=sampled)
1573
1700
 
1701
+ # Print debug output
1702
+ print("Loaded sources:")
1703
+ print(f" - Well {well_name}: {source_name_from_file}")
1704
+
1574
1705
  return self # Enable chaining
1575
1706
 
1576
1707
  def load_tops(
well_log_toolkit/well.py CHANGED
@@ -338,6 +338,7 @@ class Well:
338
338
  def load_las(
339
339
  self,
340
340
  las: Union[LasFile, str, Path, list[Union[str, Path]]],
341
+ path: Optional[Union[str, Path]] = None,
341
342
  sampled: bool = False,
342
343
  resample_method: Optional[str] = None,
343
344
  merge: bool = False,
@@ -355,7 +356,11 @@ class Well:
355
356
  Parameters
356
357
  ----------
357
358
  las : Union[LasFile, str, Path, list[Union[str, Path]]]
358
- Either a LasFile instance, path to LAS file, or list of LAS file paths
359
+ Either a LasFile instance, path to LAS file, or list of LAS file paths.
360
+ When providing a list, filenames can be relative to the path parameter.
361
+ path : Union[str, Path], optional
362
+ Directory path to prepend to all filenames. Useful when loading multiple
363
+ files from the same directory. If None, filenames are used as-is.
359
364
  sampled : bool, default False
360
365
  If True, mark all properties from this source as 'sampled' type.
361
366
  Use this for core plug data or other point measurements where
@@ -426,14 +431,36 @@ class Well:
426
431
  >>> # Merge into existing source (legacy behavior)
427
432
  >>> well.load_las("CorePor.las") # Load initial properties
428
433
  >>> well.load_las("CorePor_Extra.las", merge=True) # Merge new properties
434
+
435
+ >>> # Load multiple files from same directory
436
+ >>> well.load_las(
437
+ ... ["file1.las", "file2.las", "file3.las"],
438
+ ... path="data/well_logs",
439
+ ... combine="match",
440
+ ... source_name="AllLogs"
441
+ ... )
442
+ >>> # Loads: data/well_logs/file1.las, data/well_logs/file2.las, etc.
443
+
444
+ >>> # Mix of relative and absolute paths
445
+ >>> well.load_las(
446
+ ... ["log1.las", "log2.las"],
447
+ ... path="/absolute/path/to/logs"
448
+ ... )
429
449
  """
430
450
  # Handle list of files
431
451
  if isinstance(las, list):
452
+ # Prepend path to all filenames if provided
453
+ if path is not None:
454
+ base_path = Path(path)
455
+ las_files = [base_path / las_file for las_file in las]
456
+ else:
457
+ las_files = las
458
+
432
459
  # Load all files as separate sources
433
460
  loaded_sources = []
434
- for las_file in las:
435
- # Recursively call load_las for each file (without merge or combine)
436
- self.load_las(las_file, sampled=sampled, resample_method=resample_method, merge=False, combine=None)
461
+ for las_file in las_files:
462
+ # Recursively call load_las for each file (without merge or combine, path already prepended)
463
+ self.load_las(las_file, path=None, sampled=sampled, resample_method=resample_method, merge=False, combine=None)
437
464
  # Get the source name that was just created
438
465
  loaded_sources.append(self.sources[-1])
439
466
 
@@ -465,8 +492,14 @@ class Well:
465
492
  # Parse if path provided
466
493
  filepath = None
467
494
  if isinstance(las, (str, Path)):
468
- filepath = Path(las)
469
- las = LasFile(las)
495
+ # Prepend path if provided
496
+ if path is not None:
497
+ las_path = Path(path) / las
498
+ else:
499
+ las_path = Path(las)
500
+
501
+ filepath = las_path
502
+ las = LasFile(las_path)
470
503
  elif hasattr(las, 'filepath') and las.filepath:
471
504
  filepath = Path(las.filepath)
472
505
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: well-log-toolkit
3
- Version: 0.1.122
3
+ Version: 0.1.124
4
4
  Summary: Fast LAS file processing with lazy loading and filtering for well log analysis
5
5
  Author-email: Kristian dF Kollsgård <kkollsg@gmail.com>
6
6
  License: MIT
@@ -1,15 +1,15 @@
1
1
  well_log_toolkit/__init__.py,sha256=ilJAIIhh68pYfD9I3V53juTEJpoMN8oHpcpEFNpuXAQ,3793
2
2
  well_log_toolkit/exceptions.py,sha256=X_fzC7d4yaBFO9Vx74dEIB6xmI9Agi6_bTU3MPxn6ko,985
3
3
  well_log_toolkit/las_file.py,sha256=Tj0mRfX1aX2s6uug7BBlY1m_mu3G50EGxHGzD0eEedE,53876
4
- well_log_toolkit/manager.py,sha256=Mc_zgC9pgbYq82msiAc0KMVmPFbCX90SZK5JfwGY4H4,102422
4
+ well_log_toolkit/manager.py,sha256=oz67-IZvMEP7T_JEY7JMzeXWAvG1FmFSk8NKBif7yFI,108220
5
5
  well_log_toolkit/operations.py,sha256=z8j8fGBOwoJGUQFy-Vawjq9nm3OD_dUt0oaNh8yuG7o,18515
6
6
  well_log_toolkit/property.py,sha256=WOzoNQcmHCQ8moIKsnSyLgVC8s4LBu2x5IBXtFzmMe8,76236
7
7
  well_log_toolkit/regression.py,sha256=7D3oI-1XVlFb-mOoHTxTTtUHERFyvQSBAzJzAGVoZnk,25192
8
8
  well_log_toolkit/statistics.py,sha256=_huPMbv2H3o9ezunjEM94mJknX5wPK8V4nDv2lIZZRw,16814
9
9
  well_log_toolkit/utils.py,sha256=O2KPq4htIoUlL74V2zKftdqqTjRfezU9M-568zPLme0,6866
10
10
  well_log_toolkit/visualization.py,sha256=xb870FG5FghU2gEkqdn1b2NbWNu07oDmFDN1Cx1HIi0,157280
11
- well_log_toolkit/well.py,sha256=u2KhhMfXkMPIOKRgH3SB3k-2knotqWdBGY4Lup31Pxc,102017
12
- well_log_toolkit-0.1.122.dist-info/METADATA,sha256=MxrNyJ2HeEdC6VUmRFSPdVT6te10N5Buy-iJLzwHEFY,59810
13
- well_log_toolkit-0.1.122.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
- well_log_toolkit-0.1.122.dist-info/top_level.txt,sha256=BMOo7OKLcZEnjo0wOLMclwzwTbYKYh31I8RGDOGSBdE,17
15
- well_log_toolkit-0.1.122.dist-info/RECORD,,
11
+ well_log_toolkit/well.py,sha256=kIN3Zr0sI3Zt3DeHbxDXADbQfWnVxXCQrHZ4UEMyeqI,103343
12
+ well_log_toolkit-0.1.124.dist-info/METADATA,sha256=CfWF9CRjoyY3ZmsZ1CkKOSsuVUMg3yY3_XZE3B_lwf4,59810
13
+ well_log_toolkit-0.1.124.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ well_log_toolkit-0.1.124.dist-info/top_level.txt,sha256=BMOo7OKLcZEnjo0wOLMclwzwTbYKYh31I8RGDOGSBdE,17
15
+ well_log_toolkit-0.1.124.dist-info/RECORD,,