Exercise 15.8
Gas Gathering System - Pipeline Sizing & Station Compression
Design a gas gathering system for four wells producing 10, 15, 8, and 22 MMscf/d at wellhead pressures of 400, 350, 300, and 450 psia respectively. All wells feed into a central compressor station via pipelines of varying lengths (2, 5, 3, and 8 miles). Size each pipeline and calculate the compressor power needed to boost the gas to 1,000 psia for transmission.
---
You'll design the full gathering system for an OML cluster: pick the smallest commercial pipe that meets each well's duty, then size the central compressor that boosts the commingled stream into the transmission line. Two verified chapter functions are embedded for you; do not modify or re-derive them:
weymouth(P1, P2, D_in, L_miles, gamma_g, T_avg_F, Z_avg, E=0.92)returns a
line's capacity in MMscf/day.
compression_power(q_mmscfd, P_suction, P_discharge, T_suction_F, Z_avg, gamma_g, ...)
returns a dict with "Total HP", "Stages", "Overall ratio", etc.
Write gathering_design(wells, header_psia, transmission_psia, T_F, Z_avg, gamma_g) where wells is a list of dicts, each {'q': MMscf/d, 'whp': wellhead psia, 'L': miles}.
For each well, in list order:
- Size the flowline. Search the commercial candidates
DIAMETERS = [4, 6, 8, 10, 12, 16] (inches) and pick the smallest D such that weymouth(whp, header_psia, D, L, gamma_g, T_F, Z_avg) >= q. That is, the cheapest pipe whose Weymouth capacity meets the well's rate when it flows from its wellhead pressure down to the common gathering header_psia.
Then:
- Sum every well's
qintoq_total(MMscf/d). - Size the station: call
compression_power(q_total, header_psia, transmission_psia, T_F, Z_avg, gamma_g) this boosts the gathered gas from the low header pressure up to the transmission pressure.
Return a dict with exactly these keys:
'diameters': the list of chosenDper well, same order aswells.'q_total': total gathered rate (MMscf/d).'station': the fullcompression_powerresult dict.'total_hp':station['Total HP'].
Embedded constants from the book exercise:
WELLS = [{'q': 10.0, 'whp': 400.0, 'L': 2.0},
{'q': 15.0, 'whp': 350.0, 'L': 5.0},
{'q': 8.0, 'whp': 300.0, 'L': 3.0},
{'q': 22.0, 'whp': 450.0, 'L': 8.0}]
HEADER_PSIA = 250.0
TRANSMISSION_PSIA = 1000.0
T_F = 80.0
Z_AVG = 0.95
GAMMA_G = 0.65Finally, call it and expose the variables the tests read:
gd = gathering_design(WELLS, HEADER_PSIA, TRANSMISSION_PSIA, T_F, Z_AVG, GAMMA_G)
q_total = gd['q_total']
total_hp = gd['total_hp']
diameters = gd['diameters']> Think about it: the longer or higher-rate a well's duty, the bigger the > pipe it needs, so the 22 MMscf/d well on 8 miles should never call for a > smaller line than the 8 MMscf/d well on 3 miles. And because the station > only sees the commingled q_total, its horsepower scales linearly with > total throughput: double the gathered gas, double the HP. Pick the minimum > adequate diameter; oversizing every line passes the capacity test but wastes > millions in steel.
Stuck? Reveal hints one at a time — they progress from nudge to near-solution.
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
# ── Verified gas-pipeline / compression functions (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
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 gathering-cluster constants (do not edit) ────────────────────────
DIAMETERS = [4, 6, 8, 10, 12, 16] # commercial pipe candidates, inches
WELLS = [{'q': 10.0, 'whp': 400.0, 'L': 2.0},
{'q': 15.0, 'whp': 350.0, 'L': 5.0},
{'q': 8.0, 'whp': 300.0, 'L': 3.0},
{'q': 22.0, 'whp': 450.0, 'L': 8.0}]
HEADER_PSIA = 250.0 # common gathering-header pressure, psia
TRANSMISSION_PSIA = 1000.0 # transmission delivery pressure, psia
T_F = 80.0 # flowing temperature, deg F
Z_AVG = 0.95 # average gas Z-factor
GAMMA_G = 0.65 # gas specific gravity
def gathering_design(wells, header_psia, transmission_psia, T_F, Z_avg, gamma_g):
"""Design a gas gathering system: minimum pipe per well + station HP.
For each well, choose the smallest DIAMETERS entry whose Weymouth capacity
(from wellhead pressure down to header_psia) meets the well's rate. Then
commingle all rates and size the station compressor that boosts q_total from
header_psia up to transmission_psia.
Returns a dict with keys:
'diameters' : chosen D per well (same order as wells)
'q_total' : total gathered rate, MMscf/d
'station' : compression_power result dict
'total_hp' : station['Total HP']
"""
diameters = []
q_total = 0.0
for w in wells:
q, whp, L = w['q'], w['whp'], w['L']
chosen = None
for D in DIAMETERS:
cap = weymouth(whp, header_psia, D, L, gamma_g, T_F, Z_avg)
if cap >= q:
chosen = D
break
if chosen is None: # even the largest pipe is undersized
chosen = DIAMETERS[-1]
diameters.append(chosen)
q_total += q
station = compression_power(q_total, header_psia, transmission_psia,
T_F, Z_avg, gamma_g)
return {
'diameters': diameters,
'q_total': q_total,
'station': station,
'total_hp': station['Total HP'],
}
gd = gathering_design(WELLS, HEADER_PSIA, TRANSMISSION_PSIA, T_F, Z_AVG, GAMMA_G)
q_total = gd['q_total']
total_hp = gd['total_hp']
diameters = gd['diameters']
print("q_total (MMscf/d):", q_total)
print("diameters (in):", diameters)
print("total station HP:", total_hp)
lockCopying code is a Full Access feature.