Python package for reading and writing reservoir simulator result files including RESTART, INIT, RFT, Summary and GRID files in various formats.
—
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.
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()
"""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."""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")
"""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."""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."""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()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}")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]}")# 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