Exercise 13.6
Choke Size Optimization - Nodal Sizing at the Separator Floor
A well produces through an adjustable choke. The choke performance equation relates upstream pressure, downstream pressure, and choke diameter to flow rate. Use the simplified choke equation , where is the discharge coefficient (use 0.85), is the choke area, and is the pressure drop across the choke. Write a function that finds the choke size that maximizes oil rate while keeping downstream pressure above a minimum separator operating pressure of 100 psi.
---
We'll size the bean for a single OML well using a small but well-posed nodal model (provided for you under a do-not-edit banner; do not re-derive the physics). The book's bare choke equation q = C_d A sqrt(2*dP/rho) only tells you how much a given bean can pass; it does not by itself give an interior optimum. The missing half is the reservoir supply (the IPR): the well can only deliver q_ipr(p_down) = J * (P_RES - p_down) STB/d against the downstream pressure p_down.
Two curves, one operating point. The choke capacity q_choke(d, p_down) rises as you open the bean and as you drop the downstream pressure; the IPR supply q_ipr(p_down) rises as you drop the downstream pressure (more drawdown). The best you can do is pull the downstream pressure all the way down to the separator floor P_DOWN_MIN (maximum allowable drawdown), which fixes the optimal rate at q_opt = q_ipr(P_DOWN_MIN). The optimal bean is then the smallest diameter whose capacity meets that IPR-limited rate. Opening the bean wider than that buys nothing: the well rate plateaus at the IPR limit while you needlessly increase erosion and lose drawdown control. Oversizing the choke is the classic mistake this exercise is built to teach.
The verified model and the field constants are embedded for you. The constants are: CD = 0.85 (discharge coefficient, the book value), RHO = 53.0 lb/ft3, GC = 32.174 lbm-ft/(lbf-s^2), P_RES = 1500.0 psi (pressure feeding the choke), P_UP = 600.0 psi (tubing-head / upstream-of-choke pressure), P_DOWN_MIN = 100.0 psi (separator minimum operating pressure), and J_IPR = 2.0 STB/d per psi of drawdown.
Your task:
- Write
optimize_choke(p_down_min):
- The optimal operating point holds the downstream pressure at its floor
p_down_min (maximum IPR drawdown), so q_opt = q_ipr(p_down_min).
- Find the minimum choke diameter
d_optwhose capacity
q_choke(d, p_down_min) just meets that rate. Solve q_choke(d, p_down_min) - q_opt = 0 for d with scipy.optimize.brentq over the bracket [0.05, 5.0] inches.
- Return the tuple
(d_opt, q_opt):d_optin inches,q_optin STB/d.
- Call
optimize_choke(P_DOWN_MIN)and unpack it intod_optandq_opt
(module scope).
> Think about it: at P_DOWN_MIN = 100 psi the IPR supply is > 2 * (1500 - 100) = 2800 STB/d, and the bean that just passes it is about > 0.364 in (~23/64"). Open the bean to 0.5" and the rate does not climb > to 3000+, the IPR still caps it at 2800. Why is the smallest bean that meets > the rate the right answer rather than the biggest one you can fit?
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
from scipy.optimize import brentq
# ── Verified nodal choke model (do not edit) ─────────────────────────────
# Field-realistic Niger Delta single well. The book's bare choke equation only
# tells you how much a bean can pass; the IPR supplies the other half of the
# operating point. These helpers and constants are the verified model - use them
# exactly as given; do not re-derive the physics.
CD = 0.85 # discharge coefficient (book value)
RHO = 53.0 # oil density, lb/ft3
GC = 32.174 # gravitational constant, lbm-ft/(lbf-s^2)
P_RES = 1500.0 # pressure feeding the choke, psi
P_UP = 600.0 # upstream-of-choke / tubing-head pressure, psi
P_DOWN_MIN = 100.0 # separator minimum operating pressure, psi
J_IPR = 2.0 # productivity index, STB/d per psi of drawdown vs P_down
def choke_area_ft2(d_in):
"""Choke flow area, ft2, for a bean diameter in inches."""
return np.pi / 4.0 * (d_in / 12.0) ** 2
def q_choke(d_in, p_down):
"""STB/d the choke can pass at downstream pressure p_down (psi)."""
dP_psi = P_UP - p_down
if dP_psi <= 0:
return 0.0
v = np.sqrt(2.0 * GC * dP_psi * 144.0 / RHO) # ft/s (144 = psi -> lbf/ft2)
return CD * choke_area_ft2(d_in) * v * 86400.0 / 5.615 # ft3/s -> STB/d
def q_ipr(p_down):
"""Well supply (IPR), STB/d, at downstream pressure p_down (psi)."""
return J_IPR * (P_RES - p_down)
def optimize_choke(p_down_min):
"""Smallest bean that passes the IPR-limited rate at the separator floor.
The optimal operating point holds downstream pressure at its floor
p_down_min (max IPR drawdown), giving q_opt = q_ipr(p_down_min). Find the
MINIMUM choke diameter d_opt whose capacity q_choke(d, p_down_min) >= q_opt
(use scipy.optimize.brentq on q_choke(d, p_down_min) - q_opt over
d in [0.05, 5.0]). Return (d_opt, q_opt) with q_opt in STB/d and d_opt in
inches.
"""
q_opt = q_ipr(p_down_min)
f = lambda d: q_choke(d, p_down_min) - q_opt
d_opt = brentq(f, 0.05, 5.0)
return d_opt, q_opt
d_opt, q_opt = optimize_choke(P_DOWN_MIN)
print("optimal bean d_opt (in):", d_opt)
print("optimal rate q_opt (STB/d):", q_opt)
lockCopying code is a Full Access feature.