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

grid-operations.mddocs/

Grid Operations

Comprehensive 3D grid processing and analysis capabilities for reservoir simulation grids. Supports corner-point grids, structured grids, Local Grid Refinement (LGR), and advanced region selection operations.

Capabilities

Grid Structure and Analysis

Main grid class providing access to 3D reservoir grid structure, cell properties, and spatial operations.

class Grid:
    """3D reservoir grid operations and analysis."""
    
    def __init__(self, filename: str):
        """
        Load grid from GRID or EGRID file.
        
        Args:
            filename (str): Path to grid file (.GRID or .EGRID)
        """
    
    def save_EGRID(self, filename: str):
        """Save grid in EGRID format."""
    
    def get_name(self) -> str:
        """Get grid name."""
    
    def get_cell_dims(self) -> tuple:
        """
        Get grid dimensions.
        
        Returns:
            tuple: (nx, ny, nz) grid dimensions
        """
    
    def get_cell_volume(self, i: int, j: int, k: int) -> float:
        """
        Get cell volume.
        
        Args:
            i, j, k (int): Cell indices
            
        Returns:
            float: Cell volume in cubic meters
        """
    
    def get_cell_corner(self, i: int, j: int, k: int, corner: int) -> tuple:
        """
        Get cell corner coordinates.
        
        Args:
            i, j, k (int): Cell indices
            corner (int): Corner index (0-7)
            
        Returns:
            tuple: (x, y, z) coordinates
        """
    
    def active_index(self, i: int, j: int, k: int) -> int:
        """
        Get active index for cell.
        
        Args:
            i, j, k (int): Cell indices
            
        Returns:
            int: Active index or -1 if inactive
        """
    
    def global_index(self, i: int, j: int, k: int) -> int:
        """Get global index for cell."""
    
    def get_xyz(self, i: int, j: int, k: int) -> tuple:
        """
        Get cell center coordinates.
        
        Args:
            i, j, k (int): Cell indices
            
        Returns:
            tuple: (x, y, z) center coordinates
        """
    
    def get_corner_xyz(self, corner: int) -> tuple:
        """Get corner coordinates for all cells."""
    
    def grid_value(self, x: float, y: float, z: float) -> tuple:
        """
        Find cell containing point.
        
        Args:
            x, y, z (float): World coordinates
            
        Returns:
            tuple: (i, j, k) indices or None if outside
        """
    
    def depth(self, i: int, j: int, k: int) -> float:
        """Get cell depth (negative Z)."""
    
    def cell_invalid(self, i: int, j: int, k: int) -> bool:
        """Check if cell is invalid."""
    
    def cell_valid(self, i: int, j: int, k: int) -> bool:
        """Check if cell is valid."""
    
    def get_active_index(self, i: int, j: int, k: int) -> int:
        """Get active index (raises exception if inactive)."""
    
    def try_get_global_index(self, i: int, j: int, k: int) -> int:
        """Try to get global index, returns -1 if invalid."""
    
    def get_global_index(self, i: int, j: int, k: int) -> int:
        """Get global index (raises exception if invalid)."""
    
    def get_ijk(self, global_index: int) -> tuple:
        """
        Get IJK indices from global index.
        
        Args:
            global_index (int): Global cell index
            
        Returns:
            tuple: (i, j, k) indices
        """
    
    def get_xyz_from_ijk(self, i: int, j: int, k: int) -> tuple:
        """Get XYZ coordinates from IJK indices."""
    
    def distance(self, i1: int, j1: int, k1: int, i2: int, j2: int, k2: int) -> float:
        """Calculate distance between two cells."""
    
    def num_active(self) -> int:
        """Get number of active cells."""
    
    def get_num_active(self) -> int:
        """Get number of active cells (alias)."""
    
    def get_num_lgr(self) -> int:
        """Get number of Local Grid Refinements."""
    
    def wells(self) -> list:
        """Get list of wells intersecting the grid."""
    
    def grid_attach(self, lgr_grid):
        """Attach Local Grid Refinement."""
    
    def grid_get_lgr(self, lgr_name: str):
        """Get Local Grid Refinement by name."""

Individual Cell Operations

Access to individual cell properties and metadata.

class Cell:
    """Individual grid cell representation."""
    
    @property
    def active(self) -> bool:
        """Check if cell is active."""
    
    @property
    def i(self) -> int:
        """I-index of cell."""
    
    @property
    def j(self) -> int:
        """J-index of cell."""
    
    @property
    def k(self) -> int:
        """K-index of cell."""
    
    @property
    def global_index(self) -> int:
        """Global index of cell."""
    
    @property
    def volume(self) -> float:
        """Volume of cell."""
    
    @property
    def dimensions(self) -> tuple:
        """Cell dimensions (dx, dy, dz)."""

Region Selection and Masking

Advanced region selection capabilities for creating masks based on various criteria.

class ResdataRegion:
    """Region selection and masking operations."""
    
    def __init__(self, grid: Grid, preselect: bool = True):
        """
        Create region selector.
        
        Args:
            grid (Grid): Grid to operate on
            preselect (bool): Whether to preselect all cells
        """
    
    def select_equal(self, kw: ResdataKW, value: float):
        """Select cells where keyword equals value."""
    
    def select_less(self, kw: ResdataKW, value: float):
        """Select cells where keyword is less than value."""
    
    def select_more(self, kw: ResdataKW, value: float):
        """Select cells where keyword is greater than value."""
    
    def select_in_range(self, kw: ResdataKW, min_value: float, max_value: float):
        """Select cells where keyword is in range."""
    
    def select_active(self):
        """Select only active cells."""
    
    def select_inactive(self):
        """Select only inactive cells."""
    
    def select_small(self, kw: ResdataKW, num_cells: int):
        """Select cells with smallest keyword values."""
    
    def select_large(self, kw: ResdataKW, num_cells: int):
        """Select cells with largest keyword values."""
    
    def select_thin(self, kw: ResdataKW, num_cells: int):
        """Select thinnest cells based on keyword."""
    
    def select_thick(self, kw: ResdataKW, num_cells: int):
        """Select thickest cells based on keyword."""
    
    def select_box(self, i1: int, i2: int, j1: int, j2: int, k1: int, k2: int):
        """
        Select cells in box region.
        
        Args:
            i1, i2 (int): I-index range (inclusive)
            j1, j2 (int): J-index range (inclusive)
            k1, k2 (int): K-index range (inclusive)
        """
    
    def select_islice(self, i1: int, i2: int):
        """Select I-slice of cells."""
    
    def select_jslice(self, j1: int, j2: int):
        """Select J-slice of cells."""
    
    def select_kslice(self, k1: int, k2: int):
        """Select K-slice of cells."""
    
    def invert(self):
        """Invert current selection."""
    
    def copy(self) -> ResdataRegion:
        """Create copy of region."""
    
    def reset(self):
        """Reset selection to all cells."""

Grid Generation Utilities

Utilities for creating synthetic grids for testing and modeling.

class GridGenerator:
    """Grid generation utilities."""
    
    @classmethod
    def create_rectangular(cls, nx: int, ny: int, nz: int, 
                          dx: float, dy: float, dz: float) -> Grid:
        """
        Create rectangular grid.
        
        Args:
            nx, ny, nz (int): Grid dimensions
            dx, dy, dz (float): Cell sizes
            
        Returns:
            Grid: Generated rectangular grid
        """
    
    @classmethod  
    def create_GRDECL(cls, nx: int, ny: int, nz: int, 
                      coord: list, zcorn: list, actnum: list = None) -> Grid:
        """
        Create grid from GRDECL format data.
        
        Args:
            nx, ny, nz (int): Grid dimensions
            coord (list): Coordinate data
            zcorn (list): Z-corner data
            actnum (list, optional): Active cell flags
            
        Returns:
            Grid: Generated grid
        """

Usage Examples

Basic Grid Operations

from resdata.grid import Grid

# Load grid
grid = Grid("SIMULATION.EGRID")

# Get basic information
nx, ny, nz = grid.get_cell_dims()
print(f"Grid dimensions: {nx} x {ny} x {nz}")
print(f"Total cells: {nx * ny * nz}")
print(f"Active cells: {grid.num_active()}")

# Get cell properties
i, j, k = 10, 15, 5  # Cell indices
if grid.cell_valid(i, j, k):
    volume = grid.get_cell_volume(i, j, k)
    x, y, z = grid.get_xyz(i, j, k)
    depth = grid.depth(i, j, k)
    
    print(f"Cell ({i},{j},{k}):")
    print(f"  Volume: {volume:.2f} m³")
    print(f"  Center: ({x:.1f}, {y:.1f}, {z:.1f})")
    print(f"  Depth: {depth:.1f} m")

Region Selection

from resdata.grid import Grid, ResdataRegion
from resdata.resfile import ResdataFile

# Load grid and data
grid = Grid("SIMULATION.EGRID")
restart = ResdataFile("SIMULATION.UNRST")

# Get keyword data
porosity = restart.get_kw("PORO")
pressure = restart.get_kw("PRESSURE")

# Create region selector
region = ResdataRegion(grid, preselect=True)

# Select high porosity cells
region.select_more(porosity, 0.2)  # Porosity > 20%

# Further refine: high pressure in high porosity zone
region.select_more(pressure, 200.0)  # Pressure > 200 bar

# Select a specific layer
layer_region = ResdataRegion(grid, preselect=False)
layer_region.select_kslice(10, 10)  # Just layer 10

print(f"High porosity, high pressure cells: {region.active_size()}")
print(f"Layer 10 cells: {layer_region.active_size()}")

Working with Cell Data

from resdata.grid import Grid
from resdata.resfile import ResdataFile
import numpy as np

# Load data
grid = Grid("SIMULATION.EGRID")
restart = ResdataFile("SIMULATION.UNRST")

# Get pressure data
pressure_kw = restart.get_kw("PRESSURE")
pressure_data = pressure_kw.numpy_copy()

# Create arrays for analysis
nx, ny, nz = grid.get_cell_dims()
avg_pressure_by_layer = np.zeros(nz)

# Calculate average pressure by layer
for k in range(nz):
    layer_pressures = []
    for j in range(ny):
        for i in range(nx):
            if grid.cell_valid(i, j, k):
                active_idx = grid.active_index(i, j, k)
                if active_idx >= 0:
                    layer_pressures.append(pressure_data[active_idx])
    
    if layer_pressures:
        avg_pressure_by_layer[k] = np.mean(layer_pressures)

print("Average pressure by layer:")
for k, avg_p in enumerate(avg_pressure_by_layer):
    if avg_p > 0:
        print(f"  Layer {k+1}: {avg_p:.1f} bar")

Grid Generation

from resdata.grid import GridGenerator

# Create simple rectangular grid
grid = GridGenerator.create_rectangular(
    nx=10, ny=10, nz=5,      # 10x10x5 cells
    dx=100.0, dy=100.0, dz=10.0  # 100m x 100m x 10m cells
)

print(f"Generated grid: {grid.get_cell_dims()}")
print(f"Active cells: {grid.num_active()}")

# Check cell properties
volume = grid.get_cell_volume(5, 5, 2)  # Middle cell
x, y, z = grid.get_xyz(5, 5, 2)
print(f"Cell (5,5,2): volume={volume:.0f} m³, center=({x:.0f},{y:.0f},{z:.0f})")

Finding Cells by Location

from resdata.grid import Grid

grid = Grid("SIMULATION.EGRID")

# Find cell containing a point
x, y, z = 1000.0, 2000.0, -1500.0  # World coordinates
cell_ijk = grid.grid_value(x, y, z)

if cell_ijk:
    i, j, k = cell_ijk
    print(f"Point ({x}, {y}, {z}) is in cell ({i}, {j}, {k})")
    
    # Get cell properties
    if grid.cell_valid(i, j, k):
        volume = grid.get_cell_volume(i, j, k)
        center_x, center_y, center_z = grid.get_xyz(i, j, k)
        print(f"Cell center: ({center_x:.1f}, {center_y:.1f}, {center_z:.1f})")
        print(f"Cell volume: {volume:.2f} m³")
else:
    print(f"Point ({x}, {y}, {z}) is outside the grid")

Types

# Grid dimensions
GridDimensions = tuple[int, int, int]  # (nx, ny, nz)

# Cell coordinates
CellIndices = tuple[int, int, int]     # (i, j, k)
WorldCoordinates = tuple[float, float, float]  # (x, y, z)

# Cell properties
CellCorners = tuple[tuple[float, float, float], ...]  # 8 corner coordinates

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