CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-resdata

Python package for reading and writing reservoir simulator result files including RESTART, INIT, RFT, Summary and GRID files in various formats.

Pending
Overview
Eval results
Files

file-operations.mddocs/

File Operations

Comprehensive file I/O capabilities for reservoir simulation formats including binary Fortran files, keyword data containers, and specialized file readers. ResData supports RESTART, INIT, RFT, Summary, and GRID files in both unified and non-unified, formatted and unformatted variants.

Capabilities

Binary Fortran File I/O

Low-level binary Fortran file operations providing the foundation for reading and writing reservoir simulation data files.

class FortIO:
    """Binary Fortran file I/O operations."""
    
    # Constants
    READ_MODE: str
    WRITE_MODE: str
    READ_AND_WRITE_MODE: str
    APPEND_MODE: str
    
    def __init__(self, filename: str, mode: str = READ_MODE):
        """
        Open a Fortran binary file.
        
        Args:
            filename (str): Path to the file
            mode (str): File mode (READ_MODE, WRITE_MODE, etc.)
        """
    
    def close(self):
        """Close the file."""
    
    def get_position(self) -> int:
        """Get current file position."""
    
    def seek(self, position: int):
        """Seek to position in file."""
    
    def truncate(self, position: int):
        """Truncate file at position."""
    
    def filename(self) -> str:
        """Get filename."""
    
    def is_fortran_file(self) -> bool:
        """Check if file is a valid Fortran binary file."""
    
    def free(self):
        """Free resources."""

def openFortIO(filename: str, mode: str = FortIO.READ_MODE):
    """
    Context manager for FortIO operations.
    
    Args:
        filename (str): Path to the file
        mode (str): File mode
        
    Returns:
        FortIO: File handle
        
    Example:
        with openFortIO("data.bin") as f:
            # Use f for file operations
            position = f.get_position()
    """

Keyword Data Container

Container for simulation data keywords with comprehensive data manipulation and arithmetic operations.

class ResdataKW:
    """Container for keyword data with operations."""
    
    def __init__(self, name: str, data_type: ResDataType, size: int):
        """
        Create a new keyword.
        
        Args:
            name (str): Keyword name (max 8 characters)
            data_type (ResDataType): Data type
            size (int): Number of elements
        """
    
    def copy(self) -> ResdataKW:
        """Create a complete copy."""
    
    def slice_copy(self, start: int, stop: int) -> ResdataKW:
        """Create copy of slice."""
    
    def sub_copy(self, index_list: list) -> ResdataKW:
        """Create copy with selected indices."""
    
    @classmethod
    def read_grdecl(cls, file_handle, kw_name: str) -> ResdataKW:
        """Read GRDECL format keyword from file."""
    
    def fread(self, fortio: FortIO):
        """Read data from Fortran binary file."""
    
    def name(self) -> str:
        """Get keyword name."""
    
    def set_name(self, name: str):
        """Set keyword name."""
    
    def resize(self, new_size: int):
        """Resize the keyword data array."""
    
    def get_min_max(self) -> tuple:
        """Get (min, max) values."""
    
    def get_min(self) -> float:
        """Get minimum value."""
    
    def get_max(self) -> float:  
        """Get maximum value."""
    
    def type(self) -> ResDataType:
        """Get data type."""
    
    def data_type(self) -> ResDataType:
        """Get data type (alias for type())."""
    
    def header(self) -> tuple:
        """Get (name, size, type) header information."""
    
    def array(self) -> numpy.ndarray:
        """Get numpy array view of data."""
    
    def numpy_view(self) -> numpy.ndarray:
        """Get numpy array view (read-only)."""
    
    def numpy_copy(self) -> numpy.ndarray:
        """Get numpy array copy."""
    
    def fwrite(self, fortio: FortIO):
        """Write to Fortran binary file."""
    
    def write_grdecl(self, file_handle):
        """Write in GRDECL format."""
    
    def fprintf_data(self, file_handle, format_str: str):
        """Write formatted data to file."""
    
    def create_actnum(self) -> ResdataKW:
        """Create ACTNUM keyword from this keyword."""
    
    def fix_uninitialized(self, actnum: ResdataKW):
        """Fix uninitialized values using ACTNUM."""
    
    def first_different(self, other: ResdataKW, offset: int = 0) -> int:
        """Find first different element index."""
    
    def scatter_copy(self, target: ResdataKW, mapping: list):
        """Scatter copy using index mapping."""
    
    def safe_div(self, other: ResdataKW) -> ResdataKW:
        """Safe division avoiding division by zero."""
    
    # Arithmetic operations
    def add(self, other: ResdataKW):
        """In-place addition."""
    
    def sub(self, other: ResdataKW):
        """In-place subtraction."""
    
    def mul(self, other: ResdataKW):
        """In-place multiplication."""
    
    def div(self, other: ResdataKW):
        """In-place division."""
    
    def assign(self, other: ResdataKW):
        """Assign values from another keyword."""
    
    def apply(self, func):
        """Apply function to all elements."""
    
    def sum(self) -> float:
        """Sum all elements."""
    
    def isqrt(self):
        """In-place inverse square root."""
    
    def add_squared(self, other: ResdataKW):
        """Add squared values of other keyword."""
    
    def equal(self, other: ResdataKW) -> bool:
        """Check exact equality."""
    
    def equal_numeric(self, other: ResdataKW, epsilon: float = 1e-6) -> bool:
        """Check numeric equality within tolerance."""

Main File Reader

High-level file reader for restart format files with keyword selection and filtering capabilities.

class ResdataFile:
    """Main reader for restart format files."""
    
    def __init__(self, filename: str, flags: int = 0):
        """
        Open a restart format file.
        
        Args:
            filename (str): Path to the file
            flags (int): Optional flags for file opening
        """
    
    def close(self):
        """Close the file."""
    
    def save_kw(self, kw: ResdataKW):
        """Save keyword to file."""
    
    def select_matching_kw(self, pattern: str) -> ResdataFileView:
        """Select keywords matching pattern."""
    
    def select_from_report(self, report_step: int) -> ResdataFileView:
        """Select data from specific report step."""
    
    def select_restart_section(self, report_step: int, occurrence: int = 0) -> ResdataFileView:
        """Select restart section."""
    
    def select_global(self) -> ResdataFileView:
        """Select global keywords."""
    
    def restart_get_kw(self, kw_name: str, report_step: int, occurrence: int = 0) -> ResdataKW:
        """Get keyword from restart step."""
    
    def has_kw(self, kw_name: str) -> bool:
        """Check if keyword exists."""
    
    def num_named_kw(self, kw_name: str) -> int:
        """Count occurrences of named keyword."""
    
    def iget_named_kw(self, kw_name: str, index: int) -> ResdataKW:
        """Get named keyword by index."""
    
    def get_kw(self, kw_name: str, index: int = 0) -> ResdataKW:
        """Get keyword by name and index."""
    
    def replace_kw(self, kw: ResdataKW, kw_name: str, index: int):
        """Replace keyword in file."""
    
    def fwrite_kw(self, kw_name: str, fortio: FortIO, index: int = 0):
        """Write keyword to Fortran file."""
    
    def get_header(self) -> dict:
        """Get file header information."""
    
    def wells(self) -> list:
        """Get list of well names."""
    
    def groups(self) -> list:  
        """Get list of group names."""

def openResdataFile(filename: str):
    """
    Context manager for ResdataFile.
    
    Args:
        filename (str): Path to the file
        
    Returns:
        ResdataFile: File handle
        
    Example:
        with openResdataFile("CASE.UNRST") as f:
            pressure = f.get_kw("PRESSURE")
    """

File View Operations

Filtered view of file data enabling efficient data access and manipulation.

class ResdataFileView:
    """Filtered view into ResdataFile."""
    
    def get_kw(self, kw_name: str, index: int = 0) -> ResdataKW:
        """Get keyword from view."""
    
    def has_kw(self, kw_name: str) -> bool:
        """Check if keyword exists in view."""
    
    def size(self) -> int:
        """Get number of keywords in view."""
    
    def restart_get_kw(self, kw_name: str, report_step: int) -> ResdataKW:
        """Get restart keyword from view."""

3D Data Operations

Specialized operations for 3D grid-based data including layered access and spatial operations.

class Resdata3DKW(ResdataKW):
    """3D keyword data operations."""
    
    def get_xyz(self, grid: Grid) -> tuple:
        """Get XYZ coordinates for grid."""
    
    def get_ijk(self, grid: Grid, global_index: int) -> tuple:
        """Get IJK indices from global index."""

class Resdata3DFile(ResdataFile):
    """3D file operations."""
    
    def get_3d_kw(self, kw_name: str) -> Resdata3DKW:
        """Get 3D keyword."""

class ResdataInitFile(Resdata3DFile):
    """INIT file operations."""
    
    def get_porv(self) -> ResdataKW:
        """Get pore volume data."""
    
    def get_tranx(self) -> ResdataKW:
        """Get X-direction transmissibility."""
    
    def get_trany(self) -> ResdataKW:
        """Get Y-direction transmissibility."""
    
    def get_tranz(self) -> ResdataKW:
        """Get Z-direction transmissibility."""

class ResdataRestartFile(Resdata3DFile):
    """Restart file operations."""
    
    def unified_read(self, report_step: int) -> dict:
        """Read unified restart data for step."""
    
    def time_map(self) -> list:
        """Get time mapping for restart steps."""

Usage Examples

Reading Restart Files

from resdata.resfile import ResdataFile

# Open restart file
restart = ResdataFile("SIMULATION.UNRST")

# Get keywords
pressure = restart.get_kw("PRESSURE", 0)  # First occurrence
swat = restart.get_kw("SWAT", 0)

# Access data as numpy arrays
pressure_data = pressure.numpy_copy()
water_sat = swat.numpy_copy()

print(f"Pressure range: {pressure_data.min():.2f} - {pressure_data.max():.2f}")
print(f"Water saturation range: {water_sat.min():.3f} - {water_sat.max():.3f}")

restart.close()

Keyword Arithmetic

from resdata.resfile import ResdataFile, ResdataKW

restart = ResdataFile("SIMULATION.UNRST")

# Get oil and water saturations
soil = restart.get_kw("SOIL")
swat = restart.get_kw("SWAT")

# Calculate gas saturation (Sg = 1 - So - Sw)
sgas = soil.copy()
sgas.add(swat)  # sgas = soil + swat
sgas.mul(-1.0)  # sgas = -(soil + swat)
sgas.add(1.0)   # sgas = 1 - (soil + swat)

sgas_data = sgas.numpy_copy()
print(f"Gas saturation range: {sgas_data.min():.3f} - {sgas_data.max():.3f}")

Working with Binary Files

from resdata.resfile import FortIO, ResdataKW, ResDataType

# Create and write data
with openFortIO("output.bin", FortIO.WRITE_MODE) as f:
    # Create keyword
    pressure_kw = ResdataKW("PRESSURE", ResDataType.RD_FLOAT, 1000)
    
    # Set values
    pressure_array = pressure_kw.numpy_view()
    pressure_array[:] = 250.0  # Set all to 250 bar
    
    # Write to file
    pressure_kw.fwrite(f)

# Read data back
with openFortIO("output.bin", FortIO.READ_MODE) as f:
    read_kw = ResdataKW("PRESSURE", ResDataType.RD_FLOAT, 1000)
    read_kw.fread(f)
    
    data = read_kw.numpy_copy()
    print(f"Read {len(data)} pressure values, first value: {data[0]}")

Types

# Data types
class ResDataType:
    RD_INT: ResDataType      # Integer type
    RD_FLOAT: ResDataType    # Single precision float
    RD_DOUBLE: ResDataType   # Double precision float
    RD_BOOL: ResDataType     # Boolean type
    RD_MESS: ResDataType     # Message type
    RD_CHAR: ResDataType     # Character type
    
    @classmethod
    def RD_STRING(cls, elem_size: int) -> ResDataType:
        """String type with specified element size."""
    
    def is_int(self) -> bool: ...
    def is_float(self) -> bool: ...
    def is_double(self) -> bool: ...
    def is_bool(self) -> bool: ...
    def is_char(self) -> bool: ...
    def is_string(self) -> bool: ...
    def is_numeric(self) -> bool: ...
    def is_equal(self, other: ResDataType) -> bool: ...

# Enumerations
ResdataTypeEnum = Literal[
    "RD_CHAR_TYPE", "RD_FLOAT_TYPE", "RD_DOUBLE_TYPE", 
    "RD_INT_TYPE", "RD_BOOL_TYPE", "RD_MESS_TYPE", "RD_STRING_TYPE"
]

FileType = Literal[
    "OTHER", "RESTART", "UNIFIED_RESTART", "SUMMARY", 
    "UNIFIED_SUMMARY", "SUMMARY_HEADER", "GRID", "EGRID", "INIT", "RFT", "DATA"
]

FileMode = Literal["DEFAULT", "CLOSE_STREAM", "WRITABLE"]

Install with Tessl CLI

npx tessl i tessl/pypi-resdata

docs

file-operations.md

geometry-operations.md

gravimetry-subsidence.md

grid-operations.md

index.md

rft-plt-data.md

summary-analysis.md

utilities.md

well-data.md

tile.json