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

geometry-operations.mddocs/

Geometry Operations

Comprehensive 2D and 3D geometric operations including point sets, regions, polylines, surfaces, and spatial analysis tools for reservoir characterization and visualization.

Capabilities

Point Set Operations

Management and analysis of geometric point collections with spatial operations.

class GeoPointset:
    """Set of geometric points with spatial operations."""
    
    def __init__(self):
        """Create empty point set."""
    
    def add_point(self, x: float, y: float, z: float = 0.0):
        """
        Add point to set.
        
        Args:
            x, y (float): 2D coordinates
            z (float, optional): Z-coordinate for 3D points
        """
    
    def size(self) -> int:
        """Get number of points."""
    
    def get_point(self, index: int) -> tuple:
        """
        Get point by index.
        
        Args:
            index (int): Point index
            
        Returns:
            tuple: (x, y, z) coordinates
        """
    
    def bounding_box(self) -> tuple:
        """
        Get bounding box of all points.
        
        Returns:
            tuple: (xmin, ymin, xmax, ymax, zmin, zmax)
        """

Geographic Regions

Geographic region operations for spatial selection and containment testing.

class GeoRegion:
    """Geographic region operations and spatial queries."""
    
    def __init__(self):
        """Create empty geographic region."""
    
    def contains_point(self, x: float, y: float) -> bool:
        """
        Check if point is inside region.
        
        Args:
            x, y (float): Point coordinates
            
        Returns:
            bool: True if point is inside region
        """
    
    def select_inside(self, pointset: GeoPointset) -> list:
        """
        Select points inside region.
        
        Args:
            pointset (GeoPointset): Point set to filter
            
        Returns:
            list: Indices of points inside region
        """
    
    def select_outside(self, pointset: GeoPointset) -> list:
        """Select points outside region."""

Polyline Operations

C-based high-performance polyline operations for complex geometric calculations.

class CPolyline:
    """C-based polyline operations for high performance."""
    
    def __init__(self):
        """Create empty polyline."""
    
    def add_point(self, x: float, y: float):
        """Add point to polyline."""
    
    def size(self) -> int:
        """Get number of points."""
    
    def get_point(self, index: int) -> tuple:
        """Get point by index."""
    
    def segment_length(self, segment: int) -> float:
        """
        Get length of specific segment.
        
        Args:
            segment (int): Segment index
            
        Returns:
            float: Segment length
        """
    
    def get_length(self) -> float:
        """Get total polyline length."""
    
    def close(self):
        """Close polyline by connecting last point to first."""
    
    def extend_to_edge(self, xmin: float, ymin: float, xmax: float, ymax: float):
        """Extend polyline to bounding box edges."""

Polyline Collections

Management of multiple polylines for complex geometric structures.

class CPolylineCollection:
    """Collection of polylines for complex structures."""
    
    def __init__(self):
        """Create empty polyline collection."""
    
    def add_polyline(self, polyline: CPolyline):
        """Add polyline to collection."""
    
    def size(self) -> int:
        """Get number of polylines."""
    
    def get_polyline(self, index: int) -> CPolyline:
        """Get polyline by index."""

Python Polyline Implementation

Pure Python polyline implementation with additional convenience methods.

class Polyline:
    """Python polyline implementation with convenience methods."""
    
    def __init__(self):
        """Create empty polyline."""
    
    def add_point(self, x: float, y: float):
        """Add point to polyline."""
    
    def size(self) -> int:
        """Get number of points."""
    
    def get_points(self) -> list:
        """
        Get all points as list.
        
        Returns:
            list: List of (x, y) tuples
        """
    
    def unzip(self) -> tuple:
        """
        Separate coordinates into x and y arrays.
        
        Returns:
            tuple: (x_array, y_array)
        """
    
    def extend_to_bbox(self, bbox: tuple):
        """Extend polyline to bounding box."""

Surface Operations

3D surface representation and manipulation for reservoir characterization.

class Surface:
    """3D surface operations and analysis."""
    
    def __init__(self, nx: int, ny: int, xmin: float, ymin: float, 
                 dx: float, dy: float):
        """
        Create surface grid.
        
        Args:
            nx, ny (int): Grid dimensions
            xmin, ymin (float): Origin coordinates
            dx, dy (float): Grid spacing
        """
    
    def get_nx(self) -> int:
        """Get X-dimension size."""
    
    def get_ny(self) -> int:
        """Get Y-dimension size."""
    
    def get_value(self, x: float, y: float) -> float:
        """
        Get surface value at coordinates.
        
        Args:
            x, y (float): World coordinates
            
        Returns:
            float: Surface value (typically elevation or property)
        """
    
    def set_value(self, i: int, j: int, value: float):
        """
        Set surface value at grid indices.
        
        Args:
            i, j (int): Grid indices
            value (float): Surface value
        """
    
    def add_surface(self, other_surface):
        """Add another surface to this one."""
    
    def copy(self) -> Surface:
        """Create copy of surface."""
    
    def shift(self, offset: float):
        """Shift all surface values by offset."""
    
    def scale(self, factor: float):
        """Scale all surface values by factor."""
    
    def write(self, filename: str):
        """Write surface to file."""

Geometric Tools

Static utility functions for geometric calculations and analysis.

class GeometryTools:
    """Static geometric utility functions."""
    
    @staticmethod
    def distance(x1: float, y1: float, x2: float, y2: float) -> float:
        """
        Calculate distance between two points.
        
        Args:
            x1, y1 (float): First point coordinates
            x2, y2 (float): Second point coordinates
            
        Returns:
            float: Euclidean distance
        """
    
    @staticmethod
    def area_polygon(points: list) -> float:
        """
        Calculate polygon area.
        
        Args:
            points (list): List of (x, y) coordinate tuples
            
        Returns:
            float: Polygon area
        """
    
    @staticmethod
    def convex_hull(points: list) -> list:
        """
        Calculate convex hull of points.
        
        Args:
            points (list): List of (x, y) coordinate tuples
            
        Returns:
            list: Convex hull points
        """

XYZ File I/O

Input/output operations for XYZ coordinate data files.

class XYZIo:
    """XYZ file I/O operations."""
    
    @staticmethod
    def save(filename: str, points: list):
        """
        Save points to XYZ file.
        
        Args:
            filename (str): Output file path
            points (list): List of (x, y, z) tuples
        """
    
    @staticmethod
    def load(filename: str) -> list:
        """
        Load points from XYZ file.
        
        Args:
            filename (str): Input file path
            
        Returns:
            list: List of (x, y, z) tuples
        """

Usage Examples

Surface Analysis

from resdata.geometry import Surface, GeometryTools
import numpy as np
import matplotlib.pyplot as plt

# Create surface representing structure map
nx, ny = 50, 40
xmin, ymin = 0.0, 0.0
dx, dy = 100.0, 100.0  # 100m grid spacing

surface = Surface(nx, ny, xmin, ymin, dx, dy)

# Create synthetic structural surface (anticline)
for j in range(ny):
    for i in range(nx):
        x = xmin + i * dx
        y = ymin + j * dy
        
        # Anticline structure
        center_x, center_y = 2500.0, 2000.0
        dist_from_center = GeometryTools.distance(x, y, center_x, center_y)
        elevation = 1000.0 - (dist_from_center / 50.0) ** 2 / 100.0
        
        surface.set_value(i, j, elevation)

print(f"Surface dimensions: {surface.get_nx()} x {surface.get_ny()}")

# Sample surface values
sample_points = [(1000, 1500), (2500, 2000), (4000, 3000)]
for x, y in sample_points:
    elevation = surface.get_value(x, y)
    print(f"Elevation at ({x}, {y}): {elevation:.1f} m")

# Save surface to file
surface.write("structure_map.surf")

Polyline Operations

from resdata.geometry import CPolyline, Polyline, GeometryTools

# Create well trajectory as polyline
trajectory = CPolyline()

# Add waypoints for deviated well
waypoints = [
    (1000, 2000),  # Surface location
    (1050, 2000),  # Start of deviation
    (1200, 2050),  # Mid-point
    (1500, 2200),  # Target location
]

for x, y in waypoints:
    trajectory.add_point(x, y)

print(f"Trajectory points: {trajectory.size()}")
print(f"Total length: {trajectory.get_length():.1f} m")

# Calculate segment lengths
for i in range(trajectory.size() - 1):
    seg_length = trajectory.segment_length(i)
    print(f"Segment {i+1}: {seg_length:.1f} m")

# Create Python polyline for easier manipulation
py_trajectory = Polyline()
for x, y in waypoints:
    py_trajectory.add_point(x, y)

# Extract coordinates for plotting
points = py_trajectory.get_points()
x_coords, y_coords = py_trajectory.unzip()

# Plot trajectory
plt.figure(figsize=(10, 8))
plt.plot(x_coords, y_coords, 'bo-', linewidth=2, markersize=6)
plt.xlabel('X (m)')
plt.ylabel('Y (m)')
plt.title('Well Trajectory')
plt.grid(True)
plt.axis('equal')

# Add labels for waypoints
for i, (x, y) in enumerate(points):
    plt.annotate(f'P{i+1}', (x, y), xytext=(5, 5), textcoords='offset points')

plt.show()

Point Set Analysis

from resdata.geometry import GeoPointset, GeoRegion, GeometryTools
import numpy as np

# Create point set representing well locations
wells = GeoPointset()

# Add well locations
well_locations = [
    (1000, 1500, -2000),  # (x, y, z)
    (1200, 1800, -2100),
    (1500, 1600, -1950),
    (1800, 2000, -2200),
    (2000, 1900, -2150),
]

for x, y, z in well_locations:
    wells.add_point(x, y, z)

print(f"Number of wells: {wells.size()}")

# Get bounding box
bbox = wells.bounding_box()
print(f"Bounding box: X[{bbox[0]:.0f}, {bbox[2]:.0f}], "
      f"Y[{bbox[1]:.0f}, {bbox[3]:.0f}], Z[{bbox[4]:.0f}, {bbox[5]:.0f}]")

# Analyze well spacing
distances = []
for i in range(wells.size()):
    for j in range(i + 1, wells.size()):
        p1 = wells.get_point(i)
        p2 = wells.get_point(j)
        
        # 2D distance (ignore depth)
        dist = GeometryTools.distance(p1[0], p1[1], p2[0], p2[1])
        distances.append(dist)

if distances:
    print(f"Well spacing statistics:")
    print(f"  Minimum: {min(distances):.0f} m")
    print(f"  Maximum: {max(distances):.0f} m")
    print(f"  Average: {np.mean(distances):.0f} m")

Polygon Area Calculations

from resdata.geometry import GeometryTools
import matplotlib.pyplot as plt

# Define reservoir boundary polygon
reservoir_boundary = [
    (0, 0),
    (2000, 0),
    (2500, 1000),
    (2000, 2000),
    (1000, 2200),
    (0, 1500),
    (0, 0)  # Close polygon
]

# Calculate area
area = GeometryTools.area_polygon(reservoir_boundary)
print(f"Reservoir area: {area/1e6:.2f} km²")

# Find convex hull
convex_hull = GeometryTools.convex_hull(reservoir_boundary)
hull_area = GeometryTools.area_polygon(convex_hull)
print(f"Convex hull area: {hull_area/1e6:.2f} km²")
print(f"Concavity factor: {area/hull_area:.3f}")

# Plot polygon and convex hull
fig, ax = plt.subplots(figsize=(10, 8))

# Plot original boundary
boundary_x = [p[0] for p in reservoir_boundary]
boundary_y = [p[1] for p in reservoir_boundary]
ax.plot(boundary_x, boundary_y, 'b-', linewidth=2, label='Reservoir Boundary')
ax.fill(boundary_x, boundary_y, alpha=0.3, color='blue')

# Plot convex hull
hull_x = [p[0] for p in convex_hull] + [convex_hull[0][0]]  # Close hull
hull_y = [p[1] for p in convex_hull] + [convex_hull[0][1]]
ax.plot(hull_x, hull_y, 'r--', linewidth=2, label='Convex Hull')

ax.set_xlabel('X (m)')
ax.set_ylabel('Y (m)')
ax.set_title('Reservoir Boundary Analysis')
ax.legend()
ax.grid(True)
ax.axis('equal')
plt.show()

XYZ Data Processing

from resdata.geometry import XYZIo, Surface
import numpy as np

# Create synthetic elevation data
points = []
for i in range(100):
    # Random points with elevation trend
    x = np.random.uniform(0, 5000)
    y = np.random.uniform(0, 4000)
    z = 1000 + 0.0002 * x - 0.0001 * y + np.random.normal(0, 10)
    points.append((x, y, z))

# Save to XYZ file
XYZIo.save("elevation_data.xyz", points)

# Load data back
loaded_points = XYZIo.load("elevation_data.xyz")
print(f"Loaded {len(loaded_points)} elevation points")

# Extract coordinates for analysis
x_coords = [p[0] for p in loaded_points]
y_coords = [p[1] for p in loaded_points]
z_coords = [p[2] for p in loaded_points]

print(f"Elevation statistics:")
print(f"  Min: {min(z_coords):.1f} m")
print(f"  Max: {max(z_coords):.1f} m")
print(f"  Mean: {np.mean(z_coords):.1f} m")
print(f"  Std: {np.std(z_coords):.1f} m")

# Plot elevation map
plt.figure(figsize=(12, 8))
scatter = plt.scatter(x_coords, y_coords, c=z_coords, cmap='terrain', s=20)
plt.colorbar(scatter, label='Elevation (m)')
plt.xlabel('X (m)')
plt.ylabel('Y (m)')
plt.title('Elevation Map from XYZ Data')
plt.axis('equal')
plt.grid(True, alpha=0.3)
plt.show()

Types

# Coordinate types
Point2D = tuple[float, float]           # (x, y)
Point3D = tuple[float, float, float]    # (x, y, z)
BoundingBox = tuple[float, float, float, float, float, float]  # (xmin, ymin, xmax, ymax, zmin, zmax)

# Geometric collections
PointList = list[Point2D | Point3D]
PolygonPoints = list[Point2D]
ConvexHull = list[Point2D]

# Surface grid parameters
SurfaceGrid = tuple[int, int, float, float, float, float]  # (nx, ny, xmin, ymin, dx, dy)

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