Model Selection Guide for Reservoir Engineers

Overview

Choosing the right decline model is critical for accurate reserves estimation and forecasting. This guide provides engineering-focused guidance on when and why to use each model available in ResSmith.

Understanding Decline Behavior

Before selecting a model, understand your reservoir’s decline characteristics:

  • Exponential Decline: Constant decline rate (b = 0). Rare in practice, but occurs in some high-permeability reservoirs with strong water drive.

  • Hyperbolic Decline: Declining decline rate (0 < b < 1). Most common for oil and gas wells. The decline rate decreases over time.

  • Harmonic Decline: Special case where b = 1. Rare, but can occur in some gas wells.

  • Power Law Decline: Complex decline behavior with time-dependent decline rate. Common in unconventional plays.

  • Duong Decline: Specifically designed for tight gas and shale reservoirs with fracture-dominated flow.

Model Selection by Reservoir Type

Conventional Oil Reservoirs

Primary Choice: ARPS Hyperbolic

  • When to Use: Most conventional oil wells with 12+ months of production data

  • Physical Interpretation:

    • qi: Initial production rate (STB/day)

    • di: Initial decline rate (1/day or 1/month)

    • b: Hyperbolic exponent (typically 0.3-0.7 for oil wells)

  • Typical b-factors: 0.3-0.7 for solution gas drive, 0.5-0.8 for water drive

  • Best Practices:

    • Require at least 12 months of data for reliable fitting

    • Use fit_segmented_forecast() if decline behavior changes over time

    • Validate b-factor is between 0 and 1 (values > 1 may indicate data quality issues)

Example:

from ressmith import fit_forecast

# Conventional oil well with 24 months of data
forecast, params = fit_forecast(
    data=production_data,
    model_name='arps_hyperbolic',
    horizon=60  # 5-year forecast
)

# Check b-factor is reasonable
assert 0 < params['b'] < 1, f"Unrealistic b-factor: {params['b']}"

Unconventional (Shale/Tight) Reservoirs

Primary Choice: Power Law or Duong

  • When to Use:

    • Early-time data (< 12 months): Use Power Law or Duong

    • Mature wells (> 24 months): May transition to ARPS Hyperbolic

  • Physical Interpretation:

    • Power Law: Captures complex decline with time-dependent behavior

      • n: Decline exponent (typically 0.1-0.5 for shale)

      • Higher n values indicate stronger early-time decline

    • Duong: Designed for fracture-dominated flow

      • a: Intercept parameter

      • m: Slope parameter (typically 0.5-1.0)

  • Best Practices:

    • Use ensemble_forecast() to compare multiple models

    • Power Law often fits early-time data better than ARPS

    • Duong model specifically captures linear flow regime behavior

    • Consider using probabilistic_forecast() for uncertainty quantification

Example:

from ressmith import ensemble_forecast, compare_models

# Compare models for shale well
comparison = compare_models(
    data=shale_data,
    model_names=['power_law', 'duong', 'arps_hyperbolic'],
    horizon=120
)

# Use ensemble for uncertainty
ensemble = ensemble_forecast(
    data=shale_data,
    model_names=['power_law', 'duong'],
    horizon=120
)

Gas Reservoirs

Primary Choice: ARPS Hyperbolic or Gas-Specific Models

  • When to Use:

    • Conventional gas: ARPS Hyperbolic (b typically 0.5-1.0)

    • Tight gas: Duong or Power Law

    • Gas with strong water drive: May show exponential-like behavior

  • Physical Interpretation:

    • Gas wells often have higher b-factors than oil wells

    • Harmonic decline (b = 1) can occur but is rare

    • Use gas_reservoir_pz_method() for material balance analysis

  • Best Practices:

    • Use pressure-normalized rates for gas wells: normalize_production_with_pressure()

    • Consider using calculate_pseudopressure() for high-pressure gas

    • Validate against material balance for long-term forecasts

Waterflood Reservoirs

Primary Choice: ARPS Hyperbolic with Segmentation

  • When to Use: Wells showing multiple decline segments

  • Physical Interpretation:

    • Early segment: Primary depletion

    • Later segment: Waterflood response (often lower decline rate)

  • Best Practices:

    • Use fit_segmented_forecast() to capture phase changes

    • Monitor WOR trends: forecast_wor_gor_with_coning()

    • Use EOR workflows: analyze_waterflood(), predict_waterflood_performance()

Physical Interpretation of Parameters

ARPS Hyperbolic Parameters

  • qi (Initial Rate):

    • Represents the production rate at the start of the decline period

    • Should match observed early-time rates

    • Unrealistic values may indicate poor fit or data quality issues

  • di (Initial Decline Rate):

    • Decline rate at time zero

    • Units: 1/time (e.g., 1/month or 1/year)

    • Typical range: 0.001-0.1 per month for oil wells

    • Very high values (> 0.5/month) may indicate transient flow, not decline

  • b (Hyperbolic Exponent):

    • Controls how decline rate changes over time

    • b = 0: Exponential (constant decline)

    • 0 < b < 1: Hyperbolic (declining decline rate)

    • b = 1: Harmonic

    • b > 1: Physically unrealistic (check data quality)

    • Typical: 0.3-0.7 for oil, 0.5-1.0 for gas

Power Law Parameters

  • qi: Initial rate (same as ARPS)

  • di: Initial decline rate

  • n: Decline exponent

    • Lower n (0.1-0.3): Stronger early-time decline, common in shale

    • Higher n (0.3-0.5): More gradual decline

    • n > 0.5: May indicate transition to boundary-dominated flow

Duong Parameters

  • qi: Initial rate

  • a: Intercept parameter (typically 0.1-10)

  • m: Slope parameter

    • m ≈ 0.5: Linear flow (fracture-dominated)

    • m ≈ 1.0: Boundary-dominated flow

    • Typical range: 0.5-1.0

Common Pitfalls and How to Avoid Them

Pitfall 1: Using Exponential Model for Hyperbolic Data

Problem: Exponential model (b=0) assumes constant decline rate, which is rarely realistic.

Solution: Always start with ARPS Hyperbolic. Check if b ≈ 0, then consider exponential.

# Wrong approach
forecast_exp = fit_forecast(data, model_name='arps_exponential')

# Correct approach
forecast_hyper, params = fit_forecast(data, model_name='arps_hyperbolic')
if abs(params['b']) < 0.01:
    # Data truly shows exponential decline
    forecast_exp = fit_forecast(data, model_name='arps_exponential')

Pitfall 2: Fitting Models to Transient Flow Data

Problem: Early-time data (< 6 months) may show transient flow, not true decline.

Solution:

  • Require minimum 12 months of data for decline analysis

  • Use RTA workflows to identify flow regimes: identify_flow_regime()

  • Consider using Power Law or Duong for early-time data

# Check flow regime first
from ressmith import identify_flow_regime
regime = identify_flow_regime(time, rate)

if regime == 'transient':
    # Use RTA models, not decline models
    from ressmith import analyze_production_data
    rta_result = analyze_production_data(time, rate)
else:
    # Safe to use decline models
    forecast = fit_forecast(data, model_name='arps_hyperbolic')

Pitfall 3: Unrealistic b-Factors

Problem: b > 1 or b < 0 indicates model fit issues or data quality problems.

Solution:

  • Validate b-factor after fitting

  • Check for outliers or data quality issues

  • Consider segmented models if behavior changes

forecast, params = fit_forecast(data, model_name='arps_hyperbolic')

# Validate b-factor
if not (0 <= params['b'] <= 1):
    logger.warning(f"Unrealistic b-factor: {params['b']}. Check data quality.")
    # Try segmented model
    forecast_seg = fit_segmented_forecast(data, n_segments=2)

Pitfall 4: Ignoring Pressure Effects

Problem: Production rates should be normalized by pressure for accurate decline analysis.

Solution: Use pressure normalization workflows.

# For gas wells or variable pressure
from ressmith import normalize_production_with_pressure

normalized_data = normalize_production_with_pressure(
    data,
    rate_col='gas',
    pressure_col='pressure',
    initial_pressure=5000.0
)

# Then fit decline model
forecast = fit_forecast(normalized_data, model_name='arps_hyperbolic')

Pitfall 5: Over-Fitting with Too Many Segments

Problem: Using too many segments in segmented models can over-fit noise.

Solution:

  • Start with single segment

  • Add segments only when clear phase changes occur

  • Use walk_forward_backtest() to validate

# Start simple
forecast_simple = fit_forecast(data, model_name='arps_hyperbolic')

# Only add segments if needed
if has_clear_phase_change(data):
    forecast_seg = fit_segmented_forecast(data, n_segments=2)
    
    # Validate with backtesting
    from ressmith import walk_forward_backtest
    backtest = walk_forward_backtest(data, model_name='arps_hyperbolic')

Industry Best Practices

1. Data Quality First

  • Clean data before modeling: remove outliers, handle missing values

  • Validate temporal alignment: ensure dates are correct

  • Check for operational changes: shut-ins, workovers, etc.

from ressmith import detect_outliers

# Clean data first
cleaned_data = detect_outliers(data, method='iqr')

2. Model Validation

  • Always use walk_forward_backtest() for validation

  • Compare multiple models: compare_models()

  • Check fit diagnostics: R², RMSE, residuals

from ressmith import compare_models, walk_forward_backtest

# Compare models
comparison = compare_models(
    data,
    model_names=['arps_hyperbolic', 'power_law', 'duong']
)

# Validate best model
backtest = walk_forward_backtest(
    data,
    model_name='arps_hyperbolic',  # Best from comparison
    forecast_horizons=[12, 24, 36]
)

3. Uncertainty Quantification

  • Always use probabilistic forecasting for reserves

  • Report P10/P50/P90 scenarios

  • Use probabilistic_forecast() for uncertainty

from ressmith import probabilistic_forecast

prob_result = probabilistic_forecast(
    data,
    model_name='arps_hyperbolic',
    horizon=120,
    n_samples=1000
)

# Report reserves
eur_p10 = prob_result['p10'].sum()
eur_p50 = prob_result['p50'].sum()
eur_p90 = prob_result['p90'].sum()

4. Forecast Horizon Guidelines

  • Use 2-3x historical data length for forecasts

  • Longer horizons require more uncertainty

  • Consider economic limits in forecasts

historical_length = len(data)
forecast_horizon = min(historical_length * 2, 120)  # Max 10 years

forecast = fit_forecast(
    data,
    model_name='arps_hyperbolic',
    horizon=forecast_horizon,
    econ_limit=10.0  # Stop at 10 STB/day
)

5. Integration with Other Analysis

  • Combine decline analysis with RTA: analyze_production_data()

  • Use material balance for validation: solution_gas_drive_material_balance()

  • Consider well interference: analyze_interference_with_production_history()

Worked Example: Shale Oil Well

import pandas as pd
import numpy as np
from ressmith import (
    fit_forecast, compare_models, ensemble_forecast,
    probabilistic_forecast, estimate_eur
)

# Load production data (18 months of shale oil production)
data = pd.read_csv('shale_well_data.csv', index_col='date', parse_dates=True)

# Step 1: Compare models
comparison = compare_models(
    data,
    model_names=['arps_hyperbolic', 'power_law', 'duong'],
    horizon=120
)
print(comparison.sort_values('r_squared', ascending=False))

# Step 2: Use ensemble for uncertainty
ensemble = ensemble_forecast(
    data,
    model_names=['power_law', 'duong'],  # Best two models
    horizon=120
)

# Step 3: Probabilistic forecast
prob = probabilistic_forecast(
    data,
    model_name='power_law',  # Best single model
    horizon=120,
    n_samples=1000
)

# Step 4: Estimate EUR
eur_result = estimate_eur(
    data,
    model_name='power_law',
    t_max=360,  # 30 years
    econ_limit=5.0  # 5 STB/day economic limit
)

print(f"P50 EUR: {prob['p50'].sum():.0f} STB")
print(f"P10 EUR: {prob['p10'].sum():.0f} STB")
print(f"P90 EUR: {prob['p90'].sum():.0f} STB")
print(f"Deterministic EUR: {eur_result['eur']:.0f} STB")

Summary

  • Conventional Oil: Start with ARPS Hyperbolic (b typically 0.3-0.7)

  • Unconventional: Use Power Law or Duong for early-time, consider ARPS for mature wells

  • Gas: ARPS Hyperbolic with pressure normalization, higher b-factors (0.5-1.0)

  • Always: Validate models, quantify uncertainty, check data quality

  • Never: Fit models to transient flow data, ignore pressure effects, use unrealistic parameters

For more advanced workflows, see the Advanced Workflows Guide.