Exercise 13.4
Gas Lift Marginal Analysis - Diminishing Returns
For a well with gas lift response q = 300 + 600 × G / (200 + G), calculate the marginal oil gain (dq/dG) at injection rates of 100, 300, 500, 1000, and 2000 Mscf/d. At what injection rate does the marginal gain fall below 0.1 STB per Mscf?
---
The verified chapter function gas_lift_response(q_base, gas_inj, a, b) is embedded for you under the do-not-edit banner. It returns the diminishing-returns performance curve
q_oil = q_base + a * gas_inj / (b + gas_inj)We study the single well from the book: q = 300 + 600 × G / (200 + G), i.e. Q_BASE = 300.0 STB/d natural rate, A_GAIN = 600.0 STB/d maximum incremental oil, B_HALF = 200.0 Mscf/d half-response gas rate. You will evaluate the marginal oil gain (the slope dq/dG) at TEST_RATES = [100, 300, 500, 1000, 2000] Mscf/d, and find where the marginal gain falls below MARGINAL_THRESHOLD = 0.1 STB per Mscf.
Your tasks:
- Write
marginal_gain(gas_inj, a, b): the analytic derivative of
gas_lift_response. Differentiating a*G/(b+G) with respect to G gives
`` dq/dG = a b / (b + gas_inj)*2 # STB per Mscf ``
It must work on scalars and numpy arrays (just use the formula directly).
- Write
gas_rate_at_marginal(target_marginal, a, b): invert the marginal
curve to find the injection rate where dq/dG == target_marginal. Setting a*b/(b+G)**2 = target and solving for G:
`` G = sqrt(a * b / target_marginal) - b ``
- Assign the two output variables the tests read:
``python marginals = marginal_gain(TEST_RATES, A_GAIN, B_HALF) # array of 5 g_threshold = gas_rate_at_marginal(MARGINAL_THRESHOLD, A_GAIN, B_HALF) ``
> Think about it: the marginal gain starts at a/b = 3.0 STB/Mscf when > G = 0 and decays like 1/(b+G)**2: every extra Mscf of lift gas buys less > oil than the last. Beyond about 895 Mscf/d each additional Mscf returns less > than 0.1 STB. Why does that diminishing-returns shape mean there is always an > economic injection rate beyond which more gas is not worth pumping?
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 lift performance curve (do not edit) ────────────────────
def gas_lift_response(q_base, gas_inj, a, b):
"""
Gas lift performance curve.
Returns the total oil rate as a function of gas injection rate.
The response follows a diminishing-returns curve:
q_oil = q_base + a * gas_inj / (b + gas_inj)
Parameters
----------
q_base : float
Natural flow rate without gas lift (STB/day).
gas_inj : float
Gas injection rate (Mscf/day).
a : float
Maximum incremental oil gain (STB/day).
b : float
Gas rate at half-maximum response (Mscf/day).
"""
return q_base + a * gas_inj / (b + gas_inj)
# ── Single-well gas lift constants (do not edit) ─────────────────────────
Q_BASE = 300.0 # natural rate, STB/d
A_GAIN = 600.0 # max incremental oil, STB/d
B_HALF = 200.0 # half-response gas rate, Mscf/d
TEST_RATES = np.array([100.0, 300.0, 500.0, 1000.0, 2000.0]) # Mscf/d to evaluate
MARGINAL_THRESHOLD = 0.1 # STB per Mscf
def marginal_gain(gas_inj, a, b):
"""Return dq/dG = a*b / (b + gas_inj)**2 (STB per Mscf).
Closed-form derivative of gas_lift_response; works on scalars and
numpy arrays.
"""
return a * b / (b + gas_inj) ** 2
def gas_rate_at_marginal(target_marginal, a, b):
"""Return the injection rate G where dq/dG == target_marginal.
Invert a*b/(b+G)**2 = target -> G = sqrt(a*b/target) - b.
"""
return np.sqrt(a * b / target_marginal) - b
marginals = marginal_gain(TEST_RATES, A_GAIN, B_HALF)
g_threshold = gas_rate_at_marginal(MARGINAL_THRESHOLD, A_GAIN, B_HALF)
print("marginal gains (STB/Mscf):", marginals)
print("gas rate at 0.1 STB/Mscf threshold:", g_threshold)
lockCopying code is a Full Access feature.