Exerciseschevron_rightChapter 15chevron_right15.10
fitness_center

Exercise 15.10

Integrated Gas Field Development - Wells, Pipeline, Compression & Reserves

Level 3
Chapter 15: Gas Engineering
descriptionProblem

A gas field has estimated reserves of 500 Bscf. Design a development plan that includes: well deliverability (number of wells needed for a plateau rate of 200 MMscf/d), gathering system sizing, compression requirements, and a 200-km transmission pipeline to the delivery point. Present a cost estimate for the surface facilities.

---

This is the capstone for the gas-engineering chapter: chain four sizing calculations into a single development plan for a 500-Bscf gas field on OML 58. Every number you produce flows from a verified chapter tool; do not invent or "simplify" the correlations. The verified weymouth (pipeline capacity) and compression_power (gas horsepower) functions and the field constants are embedded for you.

Write one function:

def field_development(reserves_bscf, plateau_mmscfd, per_well_mmscfd,
                      recovery_factor, L_km, P_in, P_out, P_suction,
                      T_F, Z_avg, gamma_g) -> dict:

Build the plan in four steps:

  1. Well count. n_wells = ceil(plateau_mmscfd / per_well_mmscfd); round

up so the wells can hold the plateau (an integer).

  1. Plateau life. recoverable_mmscf = recovery_factor reserves_bscf 1000;

plateau_days = recoverable_mmscf / plateau_mmscfd; plateau_years = plateau_days / 365 (a float).

  1. Transmission pipeline. Convert the length with L_miles = L_km * 0.621371.

Search the standard line sizes DIAMETERS = [24, 30, 36, 42, 48] inches and pick the smallest D whose Weymouth capacity at the pipeline conditions meets the plateau, i.e. the smallest D with weymouth(P_in, P_out, D, L_miles, gamma_g, T_F, Z_avg) >= plateau_mmscfd. Call that D_transmission (inches).

  1. Compression station. The field gas leaves the gathering header at

P_suction and must be boosted to the pipeline inlet P_in. Size it with station = compression_power(plateau_mmscfd, P_suction, P_in, T_F, Z_avg, gamma_g) (note the argument order: rate, suction, discharge, suction temp, Z, gravity).

Return a dict with exactly these keys:

{
    "n_wells":        n_wells,          # int
    "plateau_years":  plateau_years,    # float
    "D_transmission": D_transmission,   # inches, one of DIAMETERS
    "station":        station,          # the compression_power dict
    "total_hp":       station["Total HP"],
}

The OML-58 development constants are embedded: RESERVES_BSCF = 500.0, PLATEAU_MMSCFD = 200.0, PER_WELL_MMSCFD = 25.0, RECOVERY_FACTOR = 0.80, L_KM = 200.0, P_OUT = 800.0 (delivery psia), P_IN = 1200.0 (pipeline inlet psia), P_SUCTION = 600.0 (gathering header psia, boosted up to P_IN), T_F = 80.0, Z_AVG = 0.92, GAMMA_G = 0.65.

Finally, run the plan once on the OML-58 constants and unpack the answers the tests read:

fd = field_development(RESERVES_BSCF, PLATEAU_MMSCFD, PER_WELL_MMSCFD,
                       RECOVERY_FACTOR, L_KM, P_IN, P_OUT, P_SUCTION,
                       T_F, Z_AVG, GAMMA_G)
n_wells        = fd["n_wells"]
plateau_years  = fd["plateau_years"]
d_transmission = fd["D_transmission"]
total_hp       = fd["total_hp"]

> Think about it: the plateau life is just volume conservation; > plateau_years 365 plateau_mmscfd must equal the recoverable gas > recovery_factor reserves_bscf 1000, no matter how you slice it. Pipeline > capacity grows like D^(8/3) in Weymouth, so a small bump in plateau rate can > force the next pipe size up. Why does picking the smallest adequate diameter > matter for the capital cost, and what happens to the gradient if you under-size?

lightbulbHints (0/3)

Stuck? Reveal hints one at a time — they progress from nudge to near-solution.

codeYour solution
main.py
visibilityReveal reference solutionexpand_more

Try solving it yourself first — the hints walk you through it. The solution below is one valid approach; yours may differ and still be correct.

import numpy as np
import math


# ── Verified gas pipeline capacity - Weymouth (do not edit) ──────────────
def weymouth(P1, P2, D_in, L_miles, gamma_g, T_avg_F, Z_avg, E=0.92):
    """Weymouth equation for gas pipeline capacity (MMscf/day)."""
    Tb = 520
    Pb = 14.73
    T_R = T_avg_F + 459.67

    q = 433.5 * (Tb/Pb) * E * (
        (P1**2 - P2**2) * D_in**(16/3) /
        (gamma_g * T_R * Z_avg * L_miles)
    )**0.5

    return q / 1e6


# ── Verified gas compression horsepower (do not edit) ────────────────────
def compression_power(q_mmscfd, P_suction, P_discharge, T_suction_F,
                       Z_avg, gamma_g, k=1.28, eta_adiabatic=0.82):
    """
    Calculate gas compression horsepower.

    Parameters
    ----------
    q_mmscfd : float
        Gas flow rate (MMscf/day).
    P_suction : float
        Suction pressure (psia).
    P_discharge : float
        Discharge pressure (psia).
    T_suction_F : float
        Suction temperature (deg F).
    Z_avg : float
        Average Z-factor across compressor.
    gamma_g : float
        Gas specific gravity.
    k : float
        Ratio of specific heats (Cp/Cv). ~1.28 for natural gas.
    eta_adiabatic : float
        Adiabatic efficiency (typically 0.75-0.85).

    Returns
    -------
    dict
        Compression results.
    """
    T_s = T_suction_F + 459.67  # Rankine
    r = P_discharge / P_suction  # compression ratio

    # Check if staging is needed (r > 4 typically requires multiple stages)
    max_ratio_per_stage = 4.0
    n_stages = int(np.ceil(np.log(r) / np.log(max_ratio_per_stage)))
    r_per_stage = r**(1/n_stages)

    # Adiabatic head per stage
    # HP = (q * Ts * Zs / eta) * (k/(k-1)) * [(r^((k-1)/k)) - 1]
    exponent = (k - 1) / k

    # Total power (HP) using gas horsepower equation
    hp_per_mmscfd = (
        # GPSA adiabatic gas-HP constant: 0.0857 = 3.0303 * (P_base/T_base)
        # = 3.0303 * 14.7/520. Omitting the base-condition ratio inflates HP ~35x.
        (0.0857 / eta_adiabatic) *
        (k / (k - 1)) *
        T_s * Z_avg *
        (r_per_stage**exponent - 1) *
        n_stages
    )

    total_hp = hp_per_mmscfd * q_mmscfd

    # Discharge temperature per stage
    T_discharge_R = T_s * r_per_stage**exponent
    T_discharge_F = T_discharge_R - 459.67

    return {
        "Overall ratio": round(r, 2),
        "Stages": n_stages,
        "Ratio/stage": round(r_per_stage, 2),
        "Total HP": round(total_hp, 0),
        "HP/MMscf/d": round(hp_per_mmscfd, 0),
        "T_discharge (deg F)": round(T_discharge_F, 1),
    }


# ── OML-58 gas field development constants (do not edit) ──────────────────
RESERVES_BSCF = 500.0      # estimated reserves, Bscf
PLATEAU_MMSCFD = 200.0     # target plateau rate, MMscf/d
PER_WELL_MMSCFD = 25.0     # deliverability per well, MMscf/d
RECOVERY_FACTOR = 0.80     # fraction of GIIP recovered
L_KM = 200.0               # transmission pipeline length, km
P_OUT = 800.0              # delivery (outlet) pressure, psia
P_IN = 1200.0             # pipeline inlet pressure, psia
P_SUCTION = 600.0          # gathering header (compressor suction) pressure, psia
T_F = 80.0                 # average gas temperature, deg F
Z_AVG = 0.92               # average compressibility factor
GAMMA_G = 0.65             # gas specific gravity

DIAMETERS = [24, 30, 36, 42, 48]   # standard transmission line sizes, inches


def field_development(reserves_bscf, plateau_mmscfd, per_well_mmscfd,
                      recovery_factor, L_km, P_in, P_out, P_suction,
                      T_F, Z_avg, gamma_g):
    """Integrated gas field development plan (capstone).

    Chains four sizing steps and returns a dict:
      n_wells        = ceil(plateau / per_well)                 (int)
      plateau_years  = recoverable_mmscf / plateau / 365        (float)
      D_transmission = smallest Weymouth-adequate diameter (in)
      station        = compression_power(...) dict
      total_hp       = station["Total HP"]
    """
    # 1) wells needed to hold the plateau (round UP)
    n_wells = math.ceil(plateau_mmscfd / per_well_mmscfd)

    # 2) plateau life from recoverable volume
    recoverable_mmscf = recovery_factor * reserves_bscf * 1000
    plateau_days = recoverable_mmscf / plateau_mmscfd
    plateau_years = plateau_days / 365

    # 3) smallest transmission diameter that meets the plateau
    L_miles = L_km * 0.621371
    D_transmission = None
    for D in DIAMETERS:
        if weymouth(P_in, P_out, D, L_miles, gamma_g, T_F, Z_avg) >= plateau_mmscfd:
            D_transmission = D
            break

    # 4) compression from gathering suction up to the pipeline inlet
    station = compression_power(plateau_mmscfd, P_suction, P_in, T_F, Z_avg, gamma_g)

    return dict(
        n_wells=n_wells,
        plateau_years=plateau_years,
        D_transmission=D_transmission,
        station=station,
        total_hp=station["Total HP"],
    )


fd = field_development(RESERVES_BSCF, PLATEAU_MMSCFD, PER_WELL_MMSCFD,
                       RECOVERY_FACTOR, L_KM, P_IN, P_OUT, P_SUCTION,
                       T_F, Z_AVG, GAMMA_G)
n_wells = fd["n_wells"]
plateau_years = fd["plateau_years"]
d_transmission = fd["D_transmission"]
total_hp = fd["total_hp"]

print("n_wells:", n_wells)
print("plateau_years:", plateau_years)
print("D_transmission (in):", d_transmission)
print("total_hp:", total_hp)
print("station:", fd["station"])

lockCopying code is a Full Access feature.