Core: Well and Property

Well

class logsuite.core.well.Well(name, sanitized_name, parent_manager=None)[source]

Bases: object

Single well containing multiple log properties.

Parameters:
  • name (str) – Original well name (from LAS file).

  • sanitized_name (str) – Pythonic attribute name for parent manager access.

  • parent_manager (WellDataManager, optional) – Parent manager reference.

name

Original well name.

Type:

str

sanitized_name

Sanitized name for attribute access.

Type:

str

parent_manager

Parent manager.

Type:

WellDataManager, optional

properties

List of unique property names across all sources.

Type:

list[str]

sources

List of source names (sanitized from LAS file names).

Type:

list[str]

original_las

First LAS file loaded (for template-based export).

Type:

LasFile, optional

Examples

>>> well = manager.well_12_3_2_B
>>> well.load_las("log1.las").load_las("log2.las")
>>> print(well.properties)
['PHIE', 'PERM', 'Zone', 'NTG_Flag']
>>> print(well.sources)  # ['log1.las', 'log2.las']
>>>
>>> # Add DataFrame as source
>>> df = pd.DataFrame({'DEPT': [2800, 2801], 'SW': [0.3, 0.32]})
>>> well.add_dataframe(df, unit_mappings={'SW': 'v/v'})
>>> print(well.sources)  # ['log1.las', 'log2.las', 'external_df']
>>>
>>> stats = well.phie.filter('Zone').sums_avg()
load_las(las, path=None, sampled=False, resample_method=None, merge=False, combine=None, source_name=None)[source]

Load LAS file(s) into this well, organized by source.

Properties are grouped by source (LAS file). The source name is derived from the filename stem (without extension), sanitized for Python attribute access. If the filename starts with the well name, that prefix is removed to avoid redundancy (e.g., “36_7_5_B_CorePor.las” becomes “CorePor”).

Parameters:
  • las (Union[LasFile, str, Path, list[Union[str, Path]]]) – Either a LasFile instance, path to LAS file, or list of LAS file paths. When providing a list, filenames can be relative to the path parameter.

  • path (Union[str, Path], optional) – Directory path to prepend to all filenames. Useful when loading multiple files from the same directory. If None, filenames are used as-is.

  • sampled (bool, default False) – If True, mark all properties from this source as ‘sampled’ type. Use this for core plug data or other point measurements where boundary insertion during filtering should be disabled.

  • resample_method (str, optional) – Method to use if depth grids are incompatible. Only used when loading data with different depth grids than existing data: - None (default): Will raise error if grids incompatible - ‘linear’: Linear interpolation (for continuous properties) - ‘nearest’: Nearest neighbor (for discrete/sampled) - ‘previous’: Forward-fill / previous value (for discrete) - ‘next’: Backward-fill / next value

  • merge (bool, default False) – If True and a source with the same name already exists, merge the new properties into the existing source instead of overwriting it. When merging with incompatible depth grids, resample_method must be specified. If False (default), existing source is overwritten with warning.

  • combine (str, optional) – When loading multiple files (list), combine them into a single source: - None (default): Load files as separate sources, no combining - ‘match’: Combine using match method (safest, errors on mismatch) - ‘resample’: Combine using resample method (interpolates to first file) - ‘concat’: Combine using concat method (merges all unique depths)

  • source_name (str, optional) – Name for combined source when combine is specified. If not specified, uses ‘combined_match’, ‘combined_resample’, or ‘combined_concat’

Returns:

Self for method chaining

Return type:

Well

Raises:
  • WellNameMismatchError – If LAS well name doesn’t match this well

  • WellError – If no depth column found in LAS file If merging with incompatible depths and no resample_method specified

Examples

>>> # Load single file
>>> well.load_las("36_7-5_B_CorePor.las")  # Source name: "CorePor"
>>> # Load multiple files as separate sources
>>> well.load_las(["file1.las", "file2.las", "file3.las"])
>>> print(well.sources)  # ['file1', 'file2', 'file3']
>>> # Load and combine multiple files
>>> well.load_las(
...     ["file1.las", "file2.las", "file3.las"],
...     combine="match",
...     source_name="CombinedLogs"
... )
>>> print(well.sources)  # ['CombinedLogs']
>>> # Load core plug data as sampled
>>> well.load_las("36_7-5_B_Core.las", sampled=True)
>>> # Load with resample combining
>>> well.load_las(
...     ["log1.las", "log2.las"],
...     combine="resample",
...     source_name="CombinedLogs"
... )
>>> # Merge into existing source (legacy behavior)
>>> well.load_las("CorePor.las")  # Load initial properties
>>> well.load_las("CorePor_Extra.las", merge=True)  # Merge new properties
>>> # Load multiple files from same directory
>>> well.load_las(
...     ["file1.las", "file2.las", "file3.las"],
...     path="data/well_logs",
...     combine="match",
...     source_name="AllLogs"
... )
>>> # Loads: data/well_logs/file1.las, data/well_logs/file2.las, etc.
>>> # Mix of relative and absolute paths
>>> well.load_las(
...     ["log1.las", "log2.las"],
...     path="/absolute/path/to/logs"
... )
add_dataframe(df, unit_mappings=None, type_mappings=None, label_mappings=None)[source]

Add properties from a DataFrame to this well as a new source.

The DataFrame must contain a DEPT column. All other columns will be added as properties grouped under a source named ‘external_df’, ‘external_df_1’, etc.

Parameters:
  • df (pd.DataFrame) – DataFrame containing DEPT and property columns

  • unit_mappings (dict[str, str], optional) – Mapping of property names to units (e.g., {‘PHIE’: ‘v/v’})

  • type_mappings (dict[str, str], optional) – Mapping of property names to types: ‘continuous’ or ‘discrete’ Default is ‘continuous’ for all properties

  • label_mappings (dict[str, dict[int, str]], optional) – Label mappings for discrete properties Format: {‘PropertyName’: {0: ‘Label0’, 1: ‘Label1’}}

Returns:

Self for method chaining

Return type:

Well

Examples

>>> # Create DataFrame with properties
>>> df = pd.DataFrame({
...     'DEPT': [2800, 2800.5, 2801],
...     'PHIE': [0.2, 0.25, 0.22],
...     'SW': [0.3, 0.35, 0.32],
...     'Zone': [0, 1, 1]
... })
>>>
>>> # Add to well with metadata
>>> well.add_dataframe(
...     df,
...     unit_mappings={'PHIE': 'v/v', 'SW': 'v/v', 'Zone': ''},
...     type_mappings={'Zone': 'discrete'},
...     label_mappings={'Zone': {0: 'NonReservoir', 1: 'Reservoir'}}
... )
>>>
>>> # Check sources
>>> print(well.sources)  # ['log', 'external_df']
>>> # Access properties
>>> well.external_df.Zone
rename_source(old_name, new_name)[source]

Rename a source to resolve conflicts or improve clarity.

The source is marked for rename. When the project is saved, the corresponding LAS file will be renamed on disk.

Parameters:
  • old_name (str) – Current source name

  • new_name (str) – New source name (will be sanitized)

Returns:

Self for method chaining

Return type:

Well

Raises:
  • KeyError – If old source doesn’t exist

  • ValueError – If new name already exists or conflicts

Examples

>>> well.load_las("log.las")  # Creates source named 'log'
>>> well.load_las("core.las")  # Creates source named 'core'
>>> # Both have PHIE - rename one for clarity
>>> well.rename_source("log", "wireline")
>>> well.wireline.PHIE  # Access renamed source
>>> manager.save()  # Will rename log.las to wireline.las on disk
mark_source_modified(name)[source]

Mark a source as modified so it will be re-exported on save.

This is useful if you manually modify property values and want to ensure the source is saved to disk.

Parameters:

name (str) – Source name to mark as modified

Returns:

Self for method chaining

Return type:

Well

Raises:

KeyError – If source doesn’t exist

Examples

>>> # Modify property values directly
>>> well.log.PHIE.values *= 1.1  # Scale up by 10%
>>> well.mark_source_modified("log")
>>> manager.save()  # Will re-export the modified source
remove_source(name)[source]

Remove one or more sources and all their properties from the well.

The sources are marked for deletion. When the project is saved, the corresponding LAS files will be deleted from disk.

Parameters:

name (str or list[str]) – Source name(s) to remove. Can be a single string or list of strings.

Returns:

Self for method chaining

Return type:

Well

Raises:

KeyError – If any source doesn’t exist

Examples

>>> # Remove single source
>>> well.load_las("log.las")
>>> well.load_las("core.las")
>>> well.sources  # ['log', 'core']
>>> well.remove_source("log")
>>> well.sources  # ['core']
>>> manager.save()  # Will delete log.las from project folder
>>>
>>> # Remove multiple sources
>>> well.remove_source(["log", "core"])
>>> well.sources  # []
property properties: list[str]

List of all accessible property names with their access patterns.

If a property is unique across all sources, it’s listed as-is. If a property exists in multiple sources, each is listed with source prefix. If a source name conflicts with a property name, the property needs source prefix.

Returns:

Sorted list of property access patterns

Return type:

list[str]

Examples

>>> well.properties
['PHIE', 'SW', 'log.PERM', 'core.PERM']  # PERM exists in both sources
>>> # If source named 'PHIE' exists with property 'PHIE':
>>> well.properties
['PHIE.PHIE', 'SW', 'PERM']  # PHIE property needs prefix due to source conflict
property sources: list[str]

List of all source names loaded into this well.

Source names are sanitized from LAS file names (without extension) or ‘external_df’ for DataFrames.

Returns:

List of source names (sanitized, usable as attributes)

Return type:

list[str]

Examples

>>> well.load_las("log1.las")
>>> well.add_dataframe(df)
>>> print(well.sources)  # ['log1', 'external_df']
>>> # Access sources
>>> well.log1.PHIE
>>> well.external_df.Zone
property original_las: LasFile | None

Get the first (original) LAS file loaded into this well.

Returns None if no sources have been loaded yet.

Returns:

First LasFile object loaded, or None

Return type:

Optional[LasFile]

Examples

>>> well.load_las("log1.las")
>>> original = well.original_las
>>> well.export_to_las("output.las", use_template=original)
get_property(name, source=None)[source]

Explicit property getter.

If source is specified, gets property from that source only. If source is None, gets property if it’s unique across all sources, raises error if ambiguous.

Parameters:
  • name (str) – Property name (original or sanitized)

  • source (str, optional) – Source name to get property from. If None, property must be unique.

Returns:

The requested property

Return type:

Property

Raises:

PropertyNotFoundError – If property not found or is ambiguous (exists in multiple sources)

See also

properties

List all accessible property names.

sources

List all source names.

Examples

>>> prop = well.get_property("PHIE")  # Gets PHIE if unique
>>> prop = well.get_property("PHIE", source="log")  # Gets PHIE from log source
get_intervals(name)[source]

Get saved filter intervals by name.

Parameters:

name (str) – Name of the saved filter intervals

Returns:

List of interval definitions, each with keys ‘name’, ‘top’, ‘base’

Return type:

list[dict]

Raises:

KeyError – If no intervals with this name exist

Examples

>>> # Save intervals
>>> well.PHIE.filter_intervals([
...     {"name": "Zone_A", "top": 2500, "base": 2650}
... ], save="My_Zones")
>>>
>>> # Retrieve them later
>>> intervals = well.get_intervals("My_Zones")
>>> print(intervals)
[{'name': 'Zone_A', 'top': 2500, 'base': 2650}]
property saved_intervals: list[str]

List of saved filter interval names.

Returns:

Names of all saved filter intervals

Return type:

list[str]

merge(method='match', sources=None, properties=None, depth_step=None, depth_range=None, depth_grid=None, source_name=None)[source]

Merge properties from multiple sources into a new “merged” source.

This operation replaces the selected properties with new merged versions. The original LAS files are not modified, but the in-memory properties will be replaced with the merged versions.

Parameters:
  • method ({'match', 'resample', 'concat'}, default 'match') –

    Merge method:

    • ’match’: Use first source’s depth grid as reference. Continuous properties must have exact depth match (errors otherwise). Discrete properties are automatically resampled using interval logic, since they define depth intervals. This is the safest option when depths should already align.

    • ’resample’: Use first source’s depth grid, interpolate other sources to match. If first source has regular spacing (e.g., every 0.1m), allows extrapolation for other sources. If irregular spacing, raises error when other sources extend beyond first source’s range.

    • ’concat’: Merge all unique depths, fill NaN where depth doesn’t exist

  • sources (list[str], optional) – List of source names to include (e.g., [‘CorePerm’, ‘CorePor’]) Required for ‘match’ and ‘resample’ methods. The first source is used as the reference depth grid.

  • properties (list[str], optional) – List of property names to include. If None, includes all properties

  • depth_step (float, optional) – Not used (deprecated - kept for backward compatibility)

  • depth_range (tuple[float, float], optional) – Not used (deprecated - kept for backward compatibility)

  • depth_grid (np.ndarray, optional) – Not used (deprecated - kept for backward compatibility)

  • source_name (str, optional) – Custom source name for merged properties. If None, uses ‘merged_{method}’

Returns:

Self for method chaining

Return type:

Well

Raises:
  • ValueError – If invalid method specified

  • WellError – If no properties match the filters, if ‘match’ method detects incompatible depths, or if ‘resample’ with irregular grid detects extrapolation requirements

Examples

>>> # Match sources with compatible depths (default, safest)
>>> well.merge(
...     sources=['CorePerm', 'CorePor'],
...     source_name='CorePlugs'
... )
>>> # Resample to first source's grid (allows interpolation)
>>> well.merge(
...     method='resample',
...     sources=['CompLogs', 'CorePerm'],
...     source_name='Resampled'
... )
>>> # Concatenate specific sources with custom name
>>> well.merge(
...     method='concat',
...     sources=['log1', 'log2'],
...     source_name='combined_logs'
... )
>>> # Match only specific properties
>>> well.merge(
...     sources=['source1', 'source2'],
...     properties=['PHIE', 'SW'],
...     source_name='selected_props'
... )
data(reference_property=None, include=None, exclude=None, merge_method='match', discrete_labels=True, clip_edges=True, clip_to_property=None)[source]

Export properties as DataFrame with optional merging and filtering.

This method does NOT modify the original property depth grids. Properties are temporarily aligned using the specified merge method for DataFrame output only.

Parameters:
  • reference_property (str, optional) – Property to use as depth reference. All properties will be aligned to this property’s depth grid using the merge_method. If not specified, defaults to the first property that was added (typically the first property from the first LAS file loaded).

  • include (str or list[str], optional) – Property name(s) to include. If None, includes all properties. Can be a single string or a list of strings.

  • exclude (str or list[str], optional) – Property name(s) to exclude. If both include and exclude are specified, exclude overrides (removes properties from include list). Can be a single string or a list of strings.

  • merge_method ({'match', 'resample', 'concat'}, default 'match') –

    Method to align properties to reference depth grid:

    • ’match’: Require exact depth match for continuous properties (errors if not aligned). Discrete properties are automatically resampled using interval logic. Safest option.

    • ’resample’: Interpolate properties to reference depth grid

    • ’concat’: Merge unique depths, fill NaN where missing

  • discrete_labels (bool, default True) – If True, apply label mappings to discrete properties with labels defined.

  • clip_edges (bool, default True) – If True, remove rows at the start and end where all data columns (excluding DEPT) contain NaN values. This trims the DataFrame to the range where actual data exists.

  • clip_to_property (str, optional) – Clip output to the defined range of this specific property. If specified, overrides clip_edges behavior and clips to where this property has valid data.

Returns:

DataFrame with DEPT and selected properties

Return type:

pd.DataFrame

Raises:
  • WellError – If properties have different depth grids and merge_method is ‘match’, or if merge requirements fail

  • PropertyNotFoundError – If reference_property or included properties are not found

Examples

>>> # Export all properties (errors if depths don't match exactly)
>>> df = well.data()
>>> # Export with interpolation if depths don't align
>>> df = well.data(merge_method='resample')
>>> # Export with specific reference property
>>> df = well.data(reference_property='PHIE')
>>> # Include only specific properties
>>> df = well.data(include=['PHIE', 'SW', 'PERM'])
>>> # Include single property (string or list)
>>> df = well.data(include='PHIE')  # Same as include=['PHIE']
>>> # Exclude specific properties
>>> df = well.data(exclude=['QC_Flag', 'Temp_Data'])
>>> # Exclude single property
>>> df = well.data(exclude='QC_Flag')
>>> # Include with exclusions (exclude overrides)
>>> df = well.data(include=['PHIE', 'SW', 'PERM', 'Zone'], exclude=['Zone'])
>>> # Use concat merge method to include all unique depths
>>> df = well.data(merge_method='concat')
>>> # Disable label mapping for discrete properties
>>> df = well.data(discrete_labels=False)
>>> # Disable edge clipping to keep all NaN rows
>>> df = well.data(clip_edges=False)
>>> # Clip to specific property's defined range
>>> df = well.data(clip_to_property='PHIE')
head(n=5, include=None, exclude=None)[source]

Return first n rows of well data.

Convenience method equivalent to well.data(**kwargs).head(n).

Parameters:
  • n (int, default 5) – Number of rows to return

  • include (list[str], optional) – List of property names to include

  • exclude (list[str], optional) – List of property names to exclude

Returns:

First n rows of well data

Return type:

pd.DataFrame

Examples

>>> well.head()
>>> well.head(10)
>>> well.head(include=['PHIE', 'SW'])
>>> well.head(exclude=['QC_Flag'])
to_las(include=None, exclude=None, store_labels=True, use_template=None)[source]

Convert well properties to a LasFile object.

This creates a LasFile object from the well’s properties. If properties have different depth grids, they will be automatically aligned using interpolation (resample method) to the first property’s depth grid. This ensures the exported LAS file has a consistent depth grid.

Note: Unlike the data() method which defaults to ‘match’ for safety, this method always resamples to ensure LAS export succeeds. If you need strict depth alignment checking, use well.merge(method=’match’) before calling to_las().

Parameters:
  • include (list[str], optional) – List of property names to include. If None, includes all properties.

  • exclude (list[str], optional) – List of property names to exclude. If None, no properties are excluded.

  • store_labels (bool, default True) – If True, store discrete property label mappings in the ~Parameter section.

  • use_template (Union[bool, LasFile, None], optional) – If True, uses the primary (first) LAS file as template to preserve original metadata. If a LasFile object is provided, uses that specific file as template.

Returns:

LasFile object ready for export or further manipulation

Return type:

LasFile

Raises:
  • WellError – If no properties to export or if use_template=True but no source LAS files exist

  • ValueError – If both include and exclude are specified

  • PropertyNotFoundError – If requested properties are not found

Examples

>>> # Create LasFile and export
>>> las = well.to_las(include=['PHIE', 'SW', 'PERM'])
>>> las.export('output.las')
>>> # Create LasFile with template metadata preserved
>>> las = well.to_las(use_template=True)
>>> las.export('updated.las')
>>> # Create LasFile, modify, then export
>>> las = well.to_las()
>>> # ... modify via las.set_data(df) if needed ...
>>> las.export('output.las')
>>> # If strict depth alignment is required, merge first
>>> well.merge(method='match', sources=['source1', 'source2'])
>>> las = well.to_las()
export_to_las(filepath, include=None, exclude=None, store_labels=True, null_value=-999.25, use_template=None)[source]

Export well data to LAS 2.0 format file.

This is a convenience method that calls to_las() and then exports. For more control, use to_las() to get a LasFile object first.

Parameters:
  • filepath (Union[str, Path]) – Output LAS file path

  • include (list[str], optional) – List of property names to include. If None, includes all properties.

  • exclude (list[str], optional) – List of property names to exclude. If None, no properties are excluded.

  • store_labels (bool, default True) – If True, store discrete property label mappings in the ~Parameter section. The actual data values remain numeric (standard LAS format).

  • null_value (float, default -999.25) – Value to use for missing data in LAS file

  • use_template (Union[bool, LasFile, None], optional) – If True, uses the primary (first) LAS file as template to preserve original metadata. If a LasFile object is provided, uses that specific file as template. If None or False, creates a new LAS file without template (default). Template mode preserves: ~Version info, ~Well parameters, and ~Parameter entries not related to discrete labels.

Raises:
  • WellError – If properties have different depth grids (call merge() first) If use_template=True but no source LAS files exist

  • ValueError – If both include and exclude are specified

Return type:

None

Examples

>>> # Export all properties with labels stored in parameter section
>>> well.export_to_las('output.las')
>>> # Export specific properties
>>> well.export_to_las('output.las', include=['PHIE', 'SW', 'PERM'])
>>> # Export without storing discrete labels
>>> well.export_to_las('output.las', store_labels=False)
>>> # Export using original LAS as template (preserves metadata)
>>> well.export_to_las('updated.las', use_template=True)
>>> # For more control, use to_las() then export()
>>> las = well.to_las(include=['PHIE', 'SW'])
>>> # ... modify las if needed ...
>>> las.export('output.las')
delete_renamed_sources(folder_path)[source]

Delete old LAS files for sources that were renamed.

This method is called automatically by WellDataManager.save() after exporting sources to delete the old files with the previous names.

Parameters:

folder_path (str or Path) – Folder path containing the source LAS files.

Return type:

None

Examples

>>> well.rename_source("log", "wireline")
>>> well.delete_renamed_sources("/path/to/project/well_36_7_5_B")
Renamed source: 36_7-5_B_log.las -> 36_7-5_B_wireline.las (old file deleted)
delete_marked_sources(folder_path)[source]

Delete LAS files for sources marked for deletion.

This method is called automatically by WellDataManager.save() to remove source files that were deleted using remove_source().

Parameters:

folder_path (str or Path) – Folder path containing the source LAS files.

Return type:

None

Examples

>>> well.remove_source("log")
>>> well.delete_marked_sources("/path/to/project/well_36_7_5_B")
Deleted source file: /path/to/project/well_36_7_5_B/36_7-5_B_log.las
export_sources(folder_path)[source]

Export all sources as individual LAS files to a folder.

Each source is exported as a separate LAS file with the well name prefix. Filename format: {well_sanitized_name}_{source_name}.las

Parameters:

folder_path (str or Path) – Folder path to export LAS files to. Will be created if it doesn’t exist.

Return type:

None

Examples

>>> well = manager.well_36_7_5_B
>>> well.export_sources("/path/to/output")
# Creates (hyphens preserved in filenames):
# /path/to/output/36_7-5_B_Log.las
# /path/to/output/36_7-5_B_CorePor.las
WellView(depth_range=None, tops=None, template=None, figsize=None, dpi=100, header_config=None)[source]

Create a well log display for this well.

Convenience method that creates a WellView object for visualization.

Parameters:
  • depth_range (tuple[float, float], optional) – Depth interval to display [start_depth, end_depth]. Mutually exclusive with tops parameter.

  • tops (list[str], optional) – List of formation top names to display. The depth range will be calculated automatically from the minimum and maximum depths of these tops, with 5% padding added (minimum range of 50m). Mutually exclusive with depth_range parameter. Requires that formation tops have been loaded in the well or added to the template.

  • template (Union[Template, dict, str], optional) – Display template (Template object, dict config, or name from manager)

  • figsize (tuple[float, float], optional) – Figure size (width, height) in inches

  • dpi (int, default 100) – Figure resolution

  • header_config (dict, optional) – Header styling configuration. Supported keys: - header_box_top (float): Fixed top position of header boxes - header_title_spacing (float): Vertical space between log name and scale line - header_log_spacing (float): Vertical space allocated per log - header_top_padding (float): Padding above content in header box - header_bottom_padding (float): Padding below content in header box

Returns:

Well log display object

Return type:

WellView

Examples

>>> # Simple display with default template
>>> view = well.WellView(depth_range=[2800, 3000])
>>> view.show()
>>>
>>> # Use stored template from manager
>>> view = well.WellView(depth_range=[2800, 3000], template="reservoir")
>>> view.show()
>>>
>>> # Use formation tops to auto-calculate range
>>> view = well.WellView(tops=["Top_Brent", "Top_Statfjord"], template="reservoir")
>>> view.show()
>>>
>>> # Use custom template
>>> from logsuite.visualization import Template
>>> template = Template("custom")
>>> template.add_track(
...     track_type="continuous",
...     logs=[{"name": "GR", "color": "green"}]
... )
>>> view = well.WellView(depth_range=[2800, 3000], template=template)
>>> view.save("well_log.png", dpi=300)
>>>
>>> # Customize header spacing
>>> view = well.WellView(
...     depth_range=[2800, 3000],
...     template=template,
...     header_config={"header_log_spacing": 0.04}
... )

Notes

Deprecated since version ``Well.WellView()``: violates the layered-dependency rule (core cannot import visualization). Construct directly instead: WellView(well, ...).

Crossplot(x=None, y=None, layers=None, shape=None, color=None, size=None, colortemplate='viridis', color_range=None, size_range=(20, 200), title='Cross Plot', xlabel=None, ylabel=None, figsize=(10, 8), dpi=100, marker='o', marker_size=50, marker_alpha=0.7, edge_color='black', edge_width=0.5, x_log=False, y_log=False, grid=True, grid_alpha=0.3, depth_range=None, show_colorbar=True, show_legend=True)[source]

Create a beautiful crossplot for this well.

Parameters:
  • x (str) – Name of property for x-axis

  • y (str) – Name of property for y-axis

  • shape (str, optional) – Property name for shape mapping. Default: None (single shape)

  • color (str, optional) – Property name for color mapping. Use “depth” to color by depth. Default: None (single color)

  • size (str, optional) – Property name for size mapping. Default: None (constant size)

  • colortemplate (str, optional) – Matplotlib colormap name (e.g., “viridis”, “plasma”, “coolwarm”) Default: “viridis”

  • color_range (tuple[float, float], optional) – Min and max values for color mapping. If None, uses data range. Default: None

  • size_range (tuple[float, float], optional) – Min and max marker sizes for size mapping. Default: (20, 200)

  • title (str, optional) – Plot title. Default: “Cross Plot”

  • xlabel (str, optional) – X-axis label. If None, uses property name.

  • ylabel (str, optional) – Y-axis label. If None, uses property name.

  • figsize (tuple[float, float], optional) – Figure size (width, height) in inches. Default: (10, 8)

  • dpi (int, optional) – Figure resolution. Default: 100

  • marker (str, optional) – Marker style. Default: “o”

  • marker_size (float, optional) – Base marker size. Default: 50

  • marker_alpha (float, optional) – Marker transparency (0-1). Default: 0.7

  • edge_color (str, optional) – Marker edge color. Default: “black”

  • edge_width (float, optional) – Marker edge width. Default: 0.5

  • x_log (bool, optional) – Use logarithmic scale for x-axis. Default: False

  • y_log (bool, optional) – Use logarithmic scale for y-axis. Default: False

  • grid (bool, optional) – Show grid. Default: True

  • grid_alpha (float, optional) – Grid transparency. Default: 0.3

  • depth_range (tuple[float, float], optional) – Depth range to filter data. Default: None (all depths)

  • show_colorbar (bool, optional) – Show colorbar when using color mapping. Default: True

  • show_legend (bool, optional) – Show legend when using shape mapping. Default: True

  • layers (dict[str, list[str]] | None)

Returns:

Crossplot visualization object

Return type:

Crossplot

Examples

Basic crossplot:

>>> plot = well.Crossplot(x="RHOB", y="NPHI")
>>> plot.show()

With color and size mapping:

>>> plot = well.Crossplot(
...     x="PHIE_2025",
...     y="NetSand_2025",
...     color="depth",
...     size="Sw_2025",
...     colortemplate="viridis",
...     color_range=[2000, 2500],
...     title="Porosity vs Net Sand"
... )
>>> plot.show()

With regression analysis:

>>> plot = well.Crossplot(x="RHOB", y="NPHI")
>>> plot.add_regression("linear")
>>> plot.add_regression("polynomial", degree=2, line_color="blue")
>>> plot.show()
>>> print(plot.regressions["linear"].equation())

With logarithmic scales:

>>> plot = well.Crossplot(x="PERM", y="PHIE", x_log=True)
>>> plot.show()

Notes

Deprecated since version ``Well.Crossplot()``: violates the layered-dependency rule (core cannot import visualization). Construct directly: Crossplot(well, x=..., y=...).

Property

class logsuite.core.property.Property(name, depth=None, values=None, parent_well=None, unit='', prop_type='continuous', description='', null_value=-999.25, labels=None, colors=None, styles=None, thicknesses=None, source_las=None, source_name=None, original_name=None, lazy=False)[source]

Bases: PropertyOperationsMixin

Single log property with depth-value pairs and filtering operations.

A Property can contain secondary properties (filters) that are aligned on the same depth grid. This enables chained filtering operations.

Parameters:
  • name (str) – Property name (sanitized for Python attribute access)

  • depth (np.ndarray) – Depth values

  • values (np.ndarray) – Log values

  • parent_well (Well, optional) – Parent well for property lookup during filtering

  • unit (str, default '') – Unit string

  • prop_type (str, default 'continuous') – Either ‘continuous’ or ‘discrete’

  • description (str, default '') – Property description

  • null_value (float, default -999.25) – Value to treat as null/missing

  • labels (dict[int, str], optional) – Label mapping for discrete properties (e.g., {0: ‘NonNet’, 1: ‘Net’})

  • colors (dict[int, str], optional) – Color mapping for discrete properties (e.g., {0: ‘red’, 1: ‘green’})

  • styles (dict[int, str], optional) – Line style mapping for discrete properties (e.g., {0: ‘solid’, 1: ‘dashed’})

  • thicknesses (dict[int, float], optional) – Line thickness mapping for discrete properties (e.g., {0: 1.5, 1: 2.0})

  • original_name (str, optional) – Original property name with special characters (from LAS file)

  • source_las (LasFile | None)

  • source_name (str | None)

  • lazy (bool)

name

Property name (sanitized for Python attribute access)

Type:

str

original_name

Original property name with special characters (from LAS file)

Type:

str

depth

Depth values

Type:

np.ndarray

values

Log values (nulls converted to np.nan)

Type:

np.ndarray

unit

Unit string

Type:

str

type

‘continuous’ or ‘discrete’

Type:

str

description

Description

Type:

str

parent_well

Parent well reference

Type:

Well | None

labels

Label mapping for discrete values

Type:

dict[int, str] | None

colors

Color mapping for discrete values

Type:

dict[int, str] | None

source_las

Source LAS file this property came from

Type:

LasFile | None

source

Source LAS file path (read-only property)

Type:

str | None

secondary_properties

List of aligned filter properties

Type:

list[Property]

Examples

>>> phie = well.get_property('PHIE')
>>> filtered = phie.filter('Zone').filter('NTG_Flag')
>>> stats = filtered.sums_avg()
property source: str | None

Get the source this property came from.

Returns:

Source name - either a LAS file path or external DataFrame name (e.g., ‘path/to/original.las’ or ‘external_df’)

Return type:

Optional[str]

Examples

>>> prop = well.get_property('PHIE')
>>> print(prop.source)  # 'path/to/original.las' or 'external_df'
property type: str

Get the property type (‘continuous’ or ‘discrete’).

Returns:

Property type

Return type:

str

property labels: dict[int, str] | None

Get the label mapping for discrete property values.

Returns:

Mapping of numeric values to label strings, or None if not discrete

Return type:

Optional[dict[int, str]]

property colors: dict[int, str] | None

Get the color mapping for discrete property values.

Returns:

Mapping of numeric values to color strings, or None if not defined

Return type:

Optional[dict[int, str]]

property styles: dict[int, str] | None

Get the line style mapping for discrete property values.

Returns:

Mapping of numeric values to line style strings, or None if not defined

Return type:

Optional[dict[int, str]]

property thicknesses: dict[int, float] | None

Get the line thickness mapping for discrete property values.

Returns:

Mapping of numeric values to line thickness floats, or None if not defined

Return type:

Optional[dict[int, float]]

property depth: ndarray

Get depth array with lazy loading support.

For lazy properties, data is loaded from source_las on first access. For derived properties, data is stored directly.

Returns:

Depth values

Return type:

np.ndarray

property values: ndarray

Get values array with lazy loading support.

For lazy properties, data is loaded from source_las on first access. For derived properties, data is stored directly.

Returns:

Property values (nulls converted to np.nan)

Return type:

np.ndarray

property MD: ndarray

Get Measured Depth array (standardized depth accessor).

This is an alias for .depth that provides a standardized naming convention for use in calculations and conditionals across all well log toolkit methods.

Returns:

Measured depth values (same as .depth)

Return type:

np.ndarray

Examples

>>> # Use in conditional calculations
>>> shallow_phie = np.where(well.PHIE.MD < 2000, well.PHIE, np.nan)
>>>
>>> # Combine with Well for multi-well conditionals
>>> well.calc = np.where((well.PHIE.MD > 2000) & (well.PHIE.MD < 3000),
...                      well.PHIE * 2,
...                      well.PHIE)
property Well: ndarray

Get Well name as an array aligned with depth.

Returns an array of well names, one for each depth point. This enables conditional logic based on well name in calculations and filtering operations.

Returns:

Array of well names (str), one per depth point

Return type:

np.ndarray

Examples

>>> # Use in multi-well calculations
>>> plot_data = crossplot.data  # DataFrame with 'well' column
>>>
>>> # In property calculations (returns array of well names)
>>> well_names = well.PHIE.Well
>>> # array(['36/7-5 ST2', '36/7-5 ST2', ...])
>>>
>>> # Conditional based on well
>>> result = np.where(well.PHIE.Well == "36/7-5", well.PHIE * 1.1, well.PHIE)
property is_filtered: bool

Check if this property is a filtered copy with modified depth grid.

Returns:

True if this is a filtered copy, False if original

Return type:

bool

filter_info()[source]

Get information about filtering applied to this property.

Returns:

Dictionary with filtering metadata: - is_filtered: bool - whether this is a filtered copy - filters: list[str] - names of applied filters - original_sample_count: int - samples before filtering - current_sample_count: int - samples after filtering - boundary_samples_inserted: int - synthetic samples added at boundaries

Return type:

dict

resample(target_depth)[source]

Resample property to a new depth grid using appropriate interpolation.

This method creates a new Property object with values interpolated to match the target depth grid. This is required when combining properties with different sampling rates.

Parameters:

target_depth (np.ndarray or Property) – Target depth grid to resample to. Can be: - numpy array of depth values - Property object (will use its .depth attribute)

Returns:

New property with values interpolated to target depth grid

Return type:

Property

Notes

  • Uses linear interpolation for continuous data

  • Uses forward-fill (previous) for discrete data - geological zones extend from their top/boundary until the next boundary is encountered. For example, “Cerisa West top” at 2929.93m remains active until “Cerisa West SST 1 top” at 2955.10m is intercepted.

  • Values outside the original depth range are set to NaN

  • NaN values in original data are excluded from interpolation

Examples

>>> # Resample to another property's depth grid
>>> phie_resampled = well.CorePHIE.resample(well.PHIE.depth)
>>> result = well.PHIE + phie_resampled
>>> # Or pass the property directly
>>> phie_resampled = well.CorePHIE.resample(well.PHIE)
>>> result = well.PHIE + phie_resampled
>>> # Resample to regular 0.5m grid
>>> target = np.arange(2800, 3500, 0.5)
>>> phie_regular = well.PHIE.resample(target)

See also

filter

Add discrete property as grouping dimension (auto-resamples).

apply(func, name=None)[source]

Apply a function to values, returning a new Property.

Parameters:
  • func (callable) – Function that takes a numpy array and returns a numpy array of the same shape.

  • name (str, optional) – Name for the new property. Defaults to ‘{name}_applied’.

Returns:

New property with transformed values on the same depth grid.

Return type:

Property

Examples

>>> log_perm = well.PERM.apply(np.log10, name='LOG_PERM')
>>> normalized = well.PHIE.apply(lambda v: (v - v.min()) / (v.max() - v.min()))
histogram(bins=50, weighted=True)[source]

Compute histogram of property values.

Parameters:
  • bins (int, default 50) – Number of histogram bins.

  • weighted (bool, default True) – If True, weight counts by depth intervals.

Returns:

(counts, bin_edges) — same format as numpy.histogram.

Return type:

tuple[np.ndarray, np.ndarray]

Examples

>>> counts, edges = well.PHIE.histogram(bins=20)
>>> counts, edges = well.PHIE.histogram(weighted=False)
min()[source]

Return minimum value (ignoring NaN).

Returns:

Minimum value, or NaN if all values are NaN

Return type:

float

Examples

>>> prop.min()
0.05
max()[source]

Return maximum value (ignoring NaN).

Returns:

Maximum value, or NaN if all values are NaN

Return type:

float

Examples

>>> prop.max()
0.35
mean(weighted=True)[source]

Compute mean value.

Parameters:

weighted (bool, default True) – If True, compute depth-weighted mean using interval thicknesses (default for well logs). If False, compute simple arithmetic mean (for sampled data like core points).

Returns:

Mean value, or NaN if no valid data

Return type:

float

Examples

>>> prop.mean()  # Depth-weighted by default
0.185
>>> prop.mean(weighted=False)  # Arithmetic mean
0.182
std(weighted=True)[source]

Compute standard deviation.

Parameters:

weighted (bool, default True) – If True, compute depth-weighted standard deviation (default for well logs). If False, compute simple arithmetic standard deviation (for sampled data).

Returns:

Standard deviation, or NaN if insufficient valid data

Return type:

float

Examples

>>> prop.std()  # Depth-weighted by default
0.042
>>> prop.std(weighted=False)  # Arithmetic std
0.044
percentile(p, weighted=True)[source]

Compute percentile value.

Parameters:
  • p (float) – Percentile to compute (0-100 scale). For example: - p=10 gives 10th percentile (P10) - p=50 gives median (P50) - p=90 gives 90th percentile (P90)

  • weighted (bool, default True) – If True, compute depth-weighted percentile (default for well logs). If False, compute simple arithmetic percentile (for sampled data).

Returns:

Percentile value, or NaN if no valid data

Return type:

float

Examples

>>> prop.percentile(50)  # Depth-weighted median by default
0.18
>>> prop.percentile(90)  # Depth-weighted P90
0.24
>>> prop.percentile(10, weighted=False)  # Arithmetic P10
0.09
median(weighted=True)[source]

Compute median value (50th percentile).

Parameters:

weighted (bool, default True) – If True, compute depth-weighted median (default for well logs). If False, compute simple arithmetic median (for sampled data).

Returns:

Median value, or NaN if no valid data

Return type:

float

Examples

>>> prop.median()  # Depth-weighted median by default
0.18
>>> prop.median(weighted=False)  # Arithmetic median
0.175
mode(weighted=True, bins=50)[source]

Compute mode (most frequent value).

For continuous data, values are binned before finding the mode. For discrete data, bins parameter is ignored.

Parameters:
  • weighted (bool, default True) – If True, compute depth-weighted mode (default for well logs). If False, compute simple arithmetic mode (for sampled data).

  • bins (int, default 50) – Number of bins for continuous data (ignored for discrete properties)

Returns:

Mode value, or NaN if no valid data

Return type:

float

Examples

>>> prop.mode()  # Depth-weighted mode
0.18
>>> prop.mode(weighted=False)  # Arithmetic mode
0.17
>>> discrete_prop.mode()  # For discrete: most common value
1.0
get_value(target_depth)[source]

Get the value at the closest depth point.

Finds the nearest sample point to the target depth and returns both the actual depth and the corresponding value.

Parameters:

target_depth (float) – Target depth to query

Returns:

Dictionary with keys: - ‘depth’: Actual depth of the nearest sample - ‘value’: Property value at that depth - ‘distance’: Absolute distance from target to actual depth

Return type:

dict

Examples

>>> # Get porosity at approximately 2850m
>>> result = well.PHIE.get_value(2850.0)
>>> print(result)
{'depth': 2850.5, 'value': 0.18, 'distance': 0.5}
>>> # Access the values
>>> actual_depth = result['depth']
>>> phie_value = result['value']
>>> # For discrete properties with labels
>>> zone = well.Zone.get_value(2850.0)
>>> print(zone)
{'depth': 2850.0, 'value': 0.0, 'distance': 0.0}
>>> # To get the label, use the labels dict
>>> if well.Zone.labels:
...     label = well.Zone.labels.get(int(zone['value']))
filter(property_name, insert_boundaries=None, source=None)[source]

Add a discrete property from parent well as a filter dimension.

Returns new Property with the discrete property values interpolated to the current depth grid. The depth grid remains unchanged - only the discrete values are added as a secondary property.

Parameters:
  • property_name (str) – Name of discrete property in parent well

  • insert_boundaries (bool, optional) – If True, insert synthetic samples at discrete property boundaries. Default is True for continuous properties, False for sampled properties. Set to False for sampled data (core plugs) to preserve original measurements.

  • source (str, optional) – Source name to get filter property from. If None, searches across all sources. Use this when the filter property exists in multiple sources to avoid ambiguity.

Returns:

New property instance with secondary property added (same depth grid)

Return type:

Property

Raises:

See also

sums_avg

Compute statistics grouped by filter values.

filter_intervals

Filter by depth intervals instead of discrete property.

resample

Resample to a different depth grid before filtering.

Examples

>>> filtered = well.phie.filter("Zone").filter("NTG_Flag")
>>> stats = filtered.sums_avg()
>>> # Shape remains the same - only discrete values are added
>>> original.shape  # (29, 2)
>>> filtered.data().shape  # (29, 3) - added Zone column
>>> # For sampled data (core plugs), boundaries are not inserted by default
>>> core_phie.type = 'sampled'
>>> filtered = core_phie.filter("Zone")  # No boundary insertion
>>> # When filter property is ambiguous, specify source
>>> log_phie = well.get_property("PHIE", source="log")
>>> filtered = log_phie.filter("Zone", source="log")  # Use Zone from log source
filter_intervals(intervals, name='Custom_Intervals', insert_boundaries=None, save=None)[source]

Filter by custom depth intervals defined as top/base pairs.

Each interval is processed independently, allowing overlapping intervals where the same depths can be counted in multiple zones.

Parameters:
  • intervals (list[dict] | dict[str, list[dict]] | str) –

    Interval definitions. Can be:

    • list[dict]: Direct list of intervals for the current well

    • dict[str, list[dict]]: Well-specific intervals keyed by well name. Current well must be included or raises error.

    • str: Name of a previously saved filter to use

  • name (str, default "Custom_Intervals") – Name for the filter property (used in output labels)

  • insert_boundaries (bool, optional) – If True, insert synthetic samples at interval boundaries. Default is True for continuous properties, False for sampled properties.

  • save (str, optional) – If provided, save the intervals to the well(s) under this name. Overwrites any existing filter with the same name.

Returns:

New property instance with custom intervals as filter dimension

Return type:

Property

Examples

>>> # Filter by custom zones
>>> intervals = [
...     {"name": "Zone_A", "top": 2500, "base": 2600},
...     {"name": "Zone_B", "top": 2600, "base": 2750},
... ]
>>> filtered = well.PHIE.filter_intervals(intervals)
>>> filtered.sums_avg()
>>> # Save intervals for reuse
>>> well.PHIE.filter_intervals(intervals, save="Reservoir_Zones")
>>> # Later, use saved filter by name
>>> well.PHIE.filter_intervals("Reservoir_Zones").sums_avg()
>>> # Save different intervals for multiple wells
>>> manager.well_A.PHIE.filter_intervals({
...     "well_A": intervals_a,
...     "well_B": intervals_b
... }, save="My_Zones")
>>> # Now both wells have "My_Zones" saved
>>> # Overlapping intervals - each calculated independently
>>> intervals = [
...     {"name": "Full_Reservoir", "top": 2500, "base": 2800},
...     {"name": "Upper_Only", "top": 2500, "base": 2650}
... ]
>>> # Depths 2500-2650 will be counted in BOTH zones

Notes

Intervals can overlap or have gaps. Depths outside all intervals are excluded from statistics. Overlapping intervals are calculated independently - the same depths can contribute to multiple zones.

sums_avg(weighted=None, arithmetic=None, precision=6)[source]

Compute hierarchical statistics grouped by all secondary properties.

Parameters:
  • weighted (bool, optional) – Include depth-weighted statistics. Default: True for continuous/discrete, False for sampled

  • arithmetic (bool, optional) – Include arithmetic (unweighted) statistics. Default: False for continuous/discrete, True for sampled

  • precision (int, default 6) – Number of decimal places for rounding numeric results

Returns:

Nested dictionary with statistics for each group combination. Structure includes: - mean: weighted and/or arithmetic mean - sum: weighted and/or arithmetic sum - std_dev: weighted and/or arithmetic standard deviation - percentile: {p10, p50, p90} values - range: {min, max} value range - depth_range: {min, max} depth range within the zone - samples: number of valid samples - thickness: depth interval for this group - gross_thickness: total depth interval (all groups) - thickness_fraction: thickness / gross_thickness - calculation: ‘weighted’, ‘arithmetic’, or ‘both’

Return type:

dict

Examples

>>> # Simple statistics (no filters)
>>> phie = well.get_property('PHIE')
>>> stats = phie.sums_avg()
>>> # {'mean': 0.18, 'sum': 45.2, 'samples': 251, ...}
>>> # With arithmetic stats
>>> stats = phie.sums_avg(arithmetic=True)
>>> # {'mean': {'weighted': 0.18, 'arithmetic': 0.17}, ...}
>>> # Grouped statistics
>>> filtered = phie.filter('Zone').filter('NTG_Flag')
>>> stats = filtered.sums_avg()
>>> # {'Zone_1': {'NTG_Flag_0': {...}, 'NTG_Flag_1': {...}}, ...}
>>> # Sampled data uses arithmetic by default
>>> core_phie.type = 'sampled'
>>> stats = core_phie.sums_avg()  # arithmetic=True, weighted=False
>>> # Custom precision
>>> stats = phie.sums_avg(precision=3)
>>> # {'mean': 0.180, 'sum': 45.200, ...}

See also

filter

Add a discrete property as a grouping dimension.

mean

Compute single mean value (without full statistics).

std

Compute single standard deviation.

discrete_summary(precision=6, skip=None)[source]

Compute summary statistics for discrete/categorical properties.

This method is designed for discrete logs (like facies, lithology flags, or net/gross indicators) where categorical statistics are more meaningful than continuous statistics like mean or standard deviation.

Parameters:
  • precision (int, default 6) – Number of decimal places for rounding numeric results

  • skip (list[str], optional) – List of field names to exclude from the output. Valid fields: ‘code’, ‘count’, ‘thickness’, ‘fraction’, ‘depth_range’

Returns:

Nested dictionary with statistics for each discrete value. If secondary properties (filters) exist, the structure is hierarchical.

For each discrete value, includes (unless skipped): - code: Numeric code for this category - count: Number of samples with this value - thickness: Total depth interval (meters) for this category - fraction: Proportion of total thickness (0-1) - depth_range: {min, max} depth extent

Return type:

dict

Examples

>>> # Simple discrete summary (no filters)
>>> facies = well.get_property('Facies')
>>> stats = facies.discrete_summary()
>>> # {'Sand': {'code': 1, 'count': 150, 'thickness': 25.5, 'fraction': 0.45, ...},
>>> #  'Shale': {'code': 2, 'count': 180, 'thickness': 30.8, 'fraction': 0.55, ...}}
>>> # Skip certain fields
>>> stats = facies.discrete_summary(skip=['code', 'count'])
>>> # {'Sand': {'thickness': 25.5, 'fraction': 0.45}, ...}
>>> # Grouped by zones
>>> filtered = facies.filter('Well_Tops')
>>> stats = filtered.discrete_summary()
>>> # {'Zone_A': {'Sand': {...}, 'Shale': {...}},
>>> #  'Zone_B': {'Sand': {...}, 'Shale': {...}}}

Notes

For continuous properties, use sums_avg() instead.

data(include=None, exclude=None, discrete_labels=True, clip_edges=True, clip_to_property=None)[source]

Export property and secondary properties as DataFrame.

Parameters:
  • include (str or list[str], optional) – Secondary property name(s) to include. If None, includes all. Main property is always included. Can be a single string or a list of strings.

  • exclude (str or list[str], optional) – Secondary property name(s) to exclude. If both include and exclude are specified, exclude overrides (removes from include list). Can be a single string or a list of strings.

  • discrete_labels (bool, default True) – If True, apply label mappings to discrete properties

  • clip_edges (bool, default True) – If True, remove rows at the start and end where all data columns (excluding DEPT) contain NaN values. This trims the DataFrame to the range where actual data exists.

  • clip_to_property (str, optional) – Clip output to the defined range of this specific property. If specified, overrides clip_edges behavior.

Returns:

DataFrame with DEPT, main property, and secondary properties

Return type:

pd.DataFrame

Examples

>>> filtered = well.phie.filter('Zone').filter('NTG_Flag')
>>> df = filtered.data()
>>> print(df.head())
>>> # Include only specific secondary properties
>>> df = filtered.data(include=['Zone'])
>>> # Include single secondary property (string or list)
>>> df = filtered.data(include='Zone')  # Same as include=['Zone']
>>> # Exclude specific secondary properties
>>> df = filtered.data(exclude=['NTG_Flag'])
>>> # Exclude single secondary property
>>> df = filtered.data(exclude='NTG_Flag')
>>> # Clip to property range
>>> df = filtered.data(clip_to_property='Zone')
head(n=5, include=None, exclude=None)[source]

Return first n rows of property data.

Convenience method equivalent to property.data(**kwargs).head(n).

Parameters:
  • n (int, default 5) – Number of rows to return

  • include (list[str], optional) – List of secondary property names to include

  • exclude (list[str], optional) – List of secondary property names to exclude

Returns:

First n rows of property data

Return type:

pd.DataFrame

Examples

>>> well.PHIE.head()
>>> well.PHIE.filter('Zone').head(10)
>>> well.PHIE.filter('Zone').filter('NTG').head(include=['Zone'])
export_to_las(filepath, well_name=None, store_labels=True, null_value=-999.25)[source]

Export property to LAS 2.0 format file.

Parameters:
  • filepath (Union[str, Path]) – Output LAS file path

  • well_name (str, optional) – Well name for the LAS file. If not provided and parent_well exists, uses parent well’s name. Otherwise uses ‘UNKNOWN’.

  • store_labels (bool, default True) – If True, store discrete property label mappings in the ~Parameter section. The actual data values remain numeric (standard LAS format).

  • null_value (float, default -999.25) – Value to use for missing data in LAS file

Return type:

None

Examples

>>> prop = well.get_property('PHIE')
>>> prop.export_to_las('phie_only.las')
>>> # Export with secondary properties (filters) and labels
>>> filtered = well.phie.filter('Zone').filter('NTG_Flag')
>>> filtered.export_to_las('filtered_phie.las', well_name='12/3-2 B')