Python package for reading and writing reservoir simulator result files including RESTART, INIT, RFT, Summary and GRID files in various formats.
—
Comprehensive time series data processing for reservoir simulation summary data including production rates, cumulative values, well performance, field totals, and economic analysis with NPV calculations.
Main interface for loading and accessing summary data from SMSPEC and UNSMRY files.
class Summary:
"""Summary data operations and time series analysis."""
@classmethod
def load(cls, smspec_file: str, unsmry_file: str = None) -> Summary:
"""
Load summary data from files.
Args:
smspec_file (str): Path to SMSPEC file (.SMSPEC)
unsmry_file (str, optional): Path to UNSMRY file (.UNSMRY)
If None, inferred from smspec_file
Returns:
Summary: Loaded summary data
"""
@classmethod
def writer(cls, start_time: datetime, nx: int, ny: int, nz: int) -> Summary:
"""Create summary writer for new data."""
@classmethod
def restart_writer(cls, filename: str, start_time: datetime) -> Summary:
"""Create restart summary writer."""
@classmethod
def from_pandas(cls, df: pandas.DataFrame, start_time: datetime) -> Summary:
"""Create summary from pandas DataFrame."""
def add_variable(self, key: str, num: int = 0, wgname: str = ""):
"""Add variable to summary."""
def add_t_step(self, report_step: int, sim_days: float):
"""Add time step to summary."""
def get_vector(self, key: str) -> SummaryVector:
"""
Get summary vector by key.
Args:
key (str): Summary vector key (e.g., 'FOPT', 'WOPT:PROD01')
Returns:
SummaryVector: Time series data vector
"""
def get_values(self, key: str) -> numpy.ndarray:
"""Get values array for key."""
def numpy_vector(self, key: str) -> numpy.ndarray:
"""Get numpy array of values."""
def numpy_dates(self) -> numpy.ndarray:
"""Get numpy array of dates."""
def dates(self) -> list:
"""Get list of datetime objects."""
def report_dates(self) -> list:
"""Get list of report dates."""
def pandas_frame(self, column_keys: list = None) -> pandas.DataFrame:
"""
Export to pandas DataFrame.
Args:
column_keys (list, optional): Keys to include. If None, includes all.
Returns:
pandas.DataFrame: Summary data with dates as index
"""
def get_key_index(self, key: str) -> int:
"""Get index of summary key."""
def last_value(self, key: str) -> float:
"""Get last value for key."""
def first_value(self, key: str) -> float:
"""Get first value for key."""
def wells(self) -> list:
"""Get list of well names."""
def groups(self) -> list:
"""Get list of group names."""
def report_index_list(self) -> list:
"""Get list of report indices."""Individual time series vector with interpolation and analysis capabilities.
class SummaryVector:
"""Individual summary time series vector."""
def get_name(self) -> str:
"""Get vector name/key."""
def is_total(self) -> bool:
"""Check if vector represents cumulative data."""
def is_rate(self) -> bool:
"""Check if vector represents rate data."""
def get_num_observations(self) -> int:
"""Get number of data points."""
def days_start(self) -> float:
"""Get simulation start day."""
def sim_length_days(self) -> float:
"""Get simulation length in days."""
def first_day(self) -> datetime:
"""Get first date."""
def last_day(self) -> datetime:
"""Get last date."""
def first_value(self) -> float:
"""Get first value."""
def last_value(self) -> float:
"""Get last value."""
def sum(self) -> float:
"""Sum all values."""
def get_interp(self, time: datetime) -> float:
"""
Interpolate value at specific time.
Args:
time (datetime): Time for interpolation
Returns:
float: Interpolated value
"""
def get_interp_vector(self, time_points: list) -> numpy.ndarray:
"""
Interpolate values at multiple times.
Args:
time_points (list): List of datetime objects
Returns:
numpy.ndarray: Interpolated values
"""Metadata container for summary variables providing variable information.
class SummaryNode:
"""Summary node metadata and properties."""
@property
def key(self) -> str:
"""Summary key string."""
@property
def var_type(self) -> SummaryVarType:
"""Variable type (FIELD, WELL, GROUP, etc.)."""
@property
def is_rate(self) -> bool:
"""True if rate variable."""
@property
def is_total(self) -> bool:
"""True if cumulative variable."""
@property
def unit(self) -> str:
"""Variable unit string."""
@property
def keyword(self) -> str:
"""Base keyword (e.g., 'WOPT' from 'WOPT:PROD01')."""
@property
def wgname(self) -> str:
"""Well/group name if applicable."""
@property
def num(self) -> int:
"""Numeric identifier if applicable."""Container for time step information and simulation time mapping.
class SummaryTStep:
"""Time step data container."""
def get_sim_time(self) -> float:
"""Get simulation time in days."""
def get_report(self) -> int:
"""Get report step number."""
def get_mini_step(self) -> int:
"""Get mini step number."""
def datetime(self) -> datetime:
"""Get datetime object."""Collection management for summary keywords and variable organization.
class SummaryKeyWordVector:
"""Collection of summary keywords."""
def add_keyword(self, keyword: str):
"""Add keyword to collection."""
def get_keyword(self, index: int) -> str:
"""Get keyword by index."""
def size(self) -> int:
"""Get number of keywords."""Net Present Value (NPV) calculations and economic analysis tools.
class ResdataNPV:
"""Net Present Value calculations."""
def add_price_vector(self, key: str, price_vector: NPVPriceVector):
"""
Add price vector for economic variable.
Args:
key (str): Summary key (e.g., 'FOPT')
price_vector (NPVPriceVector): Price schedule
"""
def npv_vector(self, key: str) -> SummaryVector:
"""Get NPV vector for key."""
def npv(self, key: str) -> float:
"""Get total NPV for key."""
class NPVPriceVector:
"""Price vector for NPV calculations."""
def __init__(self):
"""Create empty price vector."""
def add_price(self, date: datetime, price: float):
"""Add price point."""Comparison operations between different summary cases.
class ResdataCmp:
"""Comparison operations between summary cases."""
def add_case(self, case_name: str, summary: Summary):
"""Add case for comparison."""
def get_difference(self, key: str, case1: str, case2: str) -> SummaryVector:
"""Get difference between cases."""from resdata.summary import Summary
import matplotlib.pyplot as plt
# Load summary data
summary = Summary.load("SIMULATION.SMSPEC", "SIMULATION.UNSMRY")
# Get basic information
print(f"Wells: {summary.wells()}")
print(f"Groups: {summary.groups()}")
print(f"Simulation period: {summary.dates()[0]} to {summary.dates()[-1]}")
# Get field oil production total
fopt = summary.get_vector("FOPT")
print(f"Final field oil production: {fopt.last_value():.0f} m³")
# Get field oil production rate
fopr = summary.get_vector("FOPR")
print(f"Peak oil rate: {max(fopr.numpy_vector()):.1f} m³/day")
# Plot production profile
dates = summary.dates()
fopt_data = fopt.numpy_vector()
plt.figure(figsize=(10, 6))
plt.plot(dates, fopt_data, 'b-', linewidth=2)
plt.xlabel('Date')
plt.ylabel('Cumulative Oil Production (m³)')
plt.title('Field Oil Production')
plt.grid(True)
plt.show()from resdata.summary import Summary
import numpy as np
summary = Summary.load("SIMULATION.SMSPEC")
# Analyze well performance
wells = summary.wells()
well_performance = {}
for well in wells:
# Get well oil production total
wopt_key = f"WOPT:{well}"
if summary.get_key_index(wopt_key) >= 0:
wopt = summary.get_vector(wopt_key)
# Get well water cut
wwct_key = f"WWCT:{well}"
if summary.get_key_index(wwct_key) >= 0:
wwct = summary.get_vector(wwct_key)
final_wcut = wwct.last_value()
else:
final_wcut = 0.0
well_performance[well] = {
'total_oil': wopt.last_value(),
'final_water_cut': final_wcut,
'active_days': wopt.sim_length_days()
}
# Sort wells by oil production
sorted_wells = sorted(well_performance.items(),
key=lambda x: x[1]['total_oil'], reverse=True)
print("Well Performance Ranking:")
for i, (well, perf) in enumerate(sorted_wells[:10], 1):
print(f"{i:2d}. {well:10s}: {perf['total_oil']:8.0f} m³ oil, "
f"{perf['final_water_cut']*100:5.1f}% water cut")from resdata.summary import Summary, ResdataNPV, NPVPriceVector
from datetime import datetime
# Load summary
summary = Summary.load("SIMULATION.SMSPEC")
# Create NPV calculator
npv_calc = ResdataNPV()
# Set up oil price schedule
oil_price = NPVPriceVector()
oil_price.add_price(datetime(2020, 1, 1), 60.0) # $60/barrel
oil_price.add_price(datetime(2021, 1, 1), 70.0) # $70/barrel
oil_price.add_price(datetime(2022, 1, 1), 80.0) # $80/barrel
# Set up operating cost
opex = NPVPriceVector()
opex.add_price(datetime(2020, 1, 1), -20.0) # $20/barrel operating cost
# Add to NPV calculation
npv_calc.add_price_vector("FOPT", oil_price) # Field oil production
npv_calc.add_price_vector("FOPT", opex) # Operating costs
# Calculate NPV
total_npv = npv_calc.npv("FOPT")
print(f"Project NPV: ${total_npv:,.0f}")
# Get NPV time series
npv_vector = npv_calc.npv_vector("FOPT")
npv_data = npv_vector.numpy_vector()
dates = summary.dates()
print(f"NPV evolution:")
for i in range(0, len(dates), 365): # Annual samples
if i < len(npv_data):
print(f" {dates[i].year}: ${npv_data[i]:,.0f}")from resdata.summary import Summary
import pandas as pd
# Load summary
summary = Summary.load("SIMULATION.SMSPEC")
# Define key metrics
keys = [
'FOPT', # Field oil production total
'FGPT', # Field gas production total
'FWPT', # Field water production total
'FOPR', # Field oil production rate
'FGPR', # Field gas production rate
'FWPR', # Field water production rate
'FWCT', # Field water cut
'FGOR', # Field gas-oil ratio
]
# Export to pandas DataFrame
df = summary.pandas_frame(keys)
# Basic statistics
print("Summary Statistics:")
print(df.describe())
# Calculate monthly averages
monthly_avg = df.resample('M').mean()
print("\nMonthly Averages (last 12 months):")
print(monthly_avg.tail(12))
# Save to CSV
df.to_csv("simulation_summary.csv")
monthly_avg.to_csv("simulation_monthly_avg.csv")
# Calculate production efficiency
df['Oil_Efficiency'] = df['FOPR'] / df['FOPR'].max()
df['Production_Index'] = df['FOPR'] / (df['FWPR'] + 1) # Avoid division by zero
print(f"\nAverage oil efficiency: {df['Oil_Efficiency'].mean():.3f}")
print(f"Final production index: {df['Production_Index'].iloc[-1]:.3f}")from resdata.summary import Summary
from datetime import datetime, timedelta
import numpy as np
summary = Summary.load("SIMULATION.SMSPEC")
# Get oil production rate vector
fopr = summary.get_vector("FOPR")
# Create custom time points for interpolation
start_date = summary.dates()[0]
end_date = summary.dates()[-1]
# Monthly interpolation points
monthly_dates = []
current_date = start_date
while current_date <= end_date:
monthly_dates.append(current_date)
# Add one month (approximate)
if current_date.month == 12:
current_date = current_date.replace(year=current_date.year + 1, month=1)
else:
current_date = current_date.replace(month=current_date.month + 1)
# Interpolate values
monthly_rates = fopr.get_interp_vector(monthly_dates)
print("Monthly Oil Production Rates:")
for date, rate in zip(monthly_dates, monthly_rates):
print(f"{date.strftime('%Y-%m')}: {rate:8.1f} m³/day")
# Calculate average monthly rate
avg_monthly_rate = np.mean(monthly_rates)
print(f"\nAverage monthly rate: {avg_monthly_rate:.1f} m³/day")# Summary variable types
SummaryVarType = Literal[
"INVALID", "AQUIFER", "WELL", "REGION", "FIELD", "GROUP",
"BLOCK", "CONNECTION", "SEGMENT", "LOCAL_BLOCK",
"LOCAL_COMPLETION", "LOCAL_WELL", "NETWORK", "MISC"
]
# Common summary keys (examples)
FieldKeys = Literal[
"FOPT", # Field Oil Production Total
"FGPT", # Field Gas Production Total
"FWPT", # Field Water Production Total
"FOPR", # Field Oil Production Rate
"FGPR", # Field Gas Production Rate
"FWPR", # Field Water Production Rate
"FWCT", # Field Water Cut
"FGOR", # Field Gas-Oil Ratio
]
WellKeys = Literal[
"WOPT", # Well Oil Production Total
"WGPT", # Well Gas Production Total
"WWPT", # Well Water Production Total
"WOPR", # Well Oil Production Rate
"WGPR", # Well Gas Production Rate
"WWPR", # Well Water Production Rate
"WWCT", # Well Water Cut
"WGOR", # Well Gas-Oil Ratio
"WTHP", # Well Tubing Head Pressure
"WBHP", # Well Bottom Hole Pressure
]
# Time constants
HOURS_PER_DAY: float = 24.0
MINUTES_PER_DAY: float = 1440.0
SECONDS_PER_DAY: float = 86400.0
MUSECONDS_PER_DAY: float = 86400000000.0Install with Tessl CLI
npx tessl i tessl/pypi-resdata