Exerciseschevron_rightChapter 15chevron_right15.7
fitness_center

Exercise 15.7

Compression Staging - Horsepower & Discharge Temperature vs. Stages

Level 2
Chapter 15: Gas Engineering
descriptionProblem

For a compression ratio of 20:1, compare the total horsepower required for 1, 2, 3, and 4 stages. Plot HP vs. number of stages. Why does adding stages beyond a certain point yield diminishing returns?

---

A gathering-station compressor on OML 58 must lift wellhead gas across an overall ratio of 20:1, far above the ~4:1 a single cylinder can handle safely. The chapter's compression_power() auto-picks the stage count for you (n_stages = ceil(ln r / ln 4)), which hides the very trade-off this exercise is about. So here you force the stage count yourself and watch the horsepower, and the discharge temperature, fall as you split the ratio across more stages.

The verified compression_power() and its GPSA adiabatic constant 0.0857 are embedded for reference; do not modify them or re-derive the physics. Your job is a fixed-stage twin of the same formula.

Write staged_hp(r, n_stages, q_mmscfd, T_suction_F, Z_avg, k=1.28, eta=0.82) that returns a dict. Forcing n_stages (instead of auto-selecting it), mirror the chapter HP formula exactly:

r_per_stage  = r ** (1 / n_stages)
exponent     = (k - 1) / k
T_s          = T_suction_F + 459.67           # Rankine
hp_per       = (0.0857 / eta) * (k / (k - 1)) * T_s * Z_avg \
               * (r_per_stage ** exponent - 1) * n_stages
total_hp     = hp_per * q_mmscfd
T_discharge_F = T_s * r_per_stage ** exponent - 459.67

Return the dict with EXACTLY these keys: 'hp_per_mmscfd', 'total_hp', 'r_per_stage', 'T_discharge_F'.

Embedded constants (use them verbatim): R = 20.0 (overall ratio), Q = 50.0 (MMscf/d), T_SUCTION_F = 80.0 (deg F), Z_AVG = 0.95.

Then build the comparison the tests read:

hp_by_stage = [staged_hp(R, n, Q, T_SUCTION_F, Z_AVG)['hp_per_mmscfd']
               for n in [1, 2, 3, 4]]
hp1     = hp_by_stage[0]
hp4     = hp_by_stage[3]
tdisch1 = staged_hp(R, 1, Q, T_SUCTION_F, Z_AVG)['T_discharge_F']

> Think about it: at 20:1 a single stage runs near 580 deg F at the > discharge, hot enough to cook the lube oil and the valves. Splitting the > ratio across stages drops both the horsepower and the per-stage discharge > temperature, but the savings shrink fast: the 1→2 step buys far more than the > 3→4 step. Why does each extra stage matter less than the last?

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


# ── 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 gathering-station compression constants (do not edit) ──────────
R = 20.0            # overall compression ratio (discharge / suction)
Q = 50.0            # gas rate, MMscf/d
T_SUCTION_F = 80.0  # suction temperature, deg F
Z_AVG = 0.95        # average Z-factor across the compressor


def staged_hp(r, n_stages, q_mmscfd, T_suction_F, Z_avg, k=1.28, eta=0.82):
    """Fixed-stage adiabatic compression horsepower (FORCED n_stages).

    Same GPSA formula as compression_power, but n_stages is supplied instead
    of auto-selected, so 1/2/3/4 stages can be compared at a fixed ratio.

    Returns a dict with keys:
      'hp_per_mmscfd' : adiabatic HP per MMscf/d
      'total_hp'      : hp_per_mmscfd * q_mmscfd
      'r_per_stage'   : r ** (1 / n_stages)
      'T_discharge_F' : per-stage discharge temperature (deg F)
    """
    r_per_stage = r ** (1 / n_stages)
    exponent = (k - 1) / k
    T_s = T_suction_F + 459.67            # Rankine
    hp_per = (
        (0.0857 / eta) *
        (k / (k - 1)) *
        T_s * Z_avg *
        (r_per_stage ** exponent - 1) *
        n_stages
    )
    total_hp = hp_per * q_mmscfd
    T_discharge_F = T_s * r_per_stage ** exponent - 459.67
    return {
        "hp_per_mmscfd": hp_per,
        "total_hp": total_hp,
        "r_per_stage": r_per_stage,
        "T_discharge_F": T_discharge_F,
    }


hp_by_stage = [staged_hp(R, n, Q, T_SUCTION_F, Z_AVG)['hp_per_mmscfd']
               for n in [1, 2, 3, 4]]
hp1 = hp_by_stage[0]
hp4 = hp_by_stage[3]
tdisch1 = staged_hp(R, 1, Q, T_SUCTION_F, Z_AVG)['T_discharge_F']

print("HP/MMscf/d by stage count [1,2,3,4]:", hp_by_stage)
print("single-stage discharge temp (deg F):", tdisch1)

lockCopying code is a Full Access feature.