Exercise 14.5
Optimal WOB and RPM - Mining the Drilling Sweet Spot
For a given depth interval (constant formation), vary WOB from 10 to 45 klbf and RPM from 60 to 180. Use the drilling data to find the WOB-RPM combination that historically produced the highest ROP in that interval. Present the results as a contour plot.
---
We'll work this on a single constant-formation interval of an OML-58 well, so rock strength is fixed and ROP responds only to the controllable parameters WOB (weight on bit) and RPM (rotary speed). A synthetic interval DataFrame is built for you with np.random.seed(11) and 2,000 bottomhole records where ROP rises with both WOB and RPM but rolls over at high values (there is a real interior sweet spot) plus measurement noise. Do not modify the generator or re-derive the physics; your job is to mine the data, not regenerate it.
The bin edges are fixed constants:
WOB_BINS = np.arange(10, 50, 5)→ edges10, 15, 20, 25, 30, 35, 40, 45RPM_BINS = np.arange(60, 200, 20)→ edges60, 80, 100, 120, 140, 160, 180
Your tasks:
- Write
rop_grid(df, wob_bins=WOB_BINS, rpm_bins=RPM_BINS):
- Bin each row's WOB with
pd.cut(df["WOB"], bins=wob_bins)and its RPM with
pd.cut(df["RPM"], bins=rpm_bins).
- Group by both bins and take the mean ROP in each cell.
- Return a 2-D pandas table (a pivot /
unstack) whose rows are WOB bins
and columns are RPM bins, with mean ROP as the values.
- Write
optimal_wob_rpm(df, wob_bins=WOB_BINS, rpm_bins=RPM_BINS):
- Find the single cell with the highest mean ROP.
- Return the tuple
(best_wob, best_rpm, best_rop)wherebest_woband
best_rpm are the two pandas Interval objects of the winning cell and best_rop is that cell's mean ROP as a float.
- Build the table and find the optimum on the provided
intervaldata:
grid = rop_grid(interval)best_wob, best_rpm, best_rop = optimal_wob_rpm(interval)
The output variables the tests read are exactly: grid (the table), best_wob and best_rpm (the two pandas Interval objects), and best_rop (a float).
> Think about it: for this interval the highest mean ROP lands in the > high-WOB / high-RPM corner, around 80.6 ft/hr in cell WOB (40, 45], > RPM (160, 180]. But that corner is a property of this formation's data, not > a universal answer; feed in a different interval and the sweet spot moves. Your > function must read it off the grid every time, never assume the corner.
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
import pandas as pd
# ── Verified constant-formation interval generator + bin edges (do not edit) ──
# A single OML-58 drilling interval: rock strength is fixed, so ROP responds only
# to WOB and RPM. ROP rises with both but rolls over at high values (a real
# interior sweet spot) plus N(0, 3) measurement noise. Seeded for reproducibility.
WOB_BINS = np.arange(10, 50, 5) # edges 10, 15, 20, 25, 30, 35, 40, 45
RPM_BINS = np.arange(60, 200, 20) # edges 60, 80, 100, 120, 140, 160, 180
def make_interval():
"""Build the constant-formation interval DataFrame (do not edit)."""
np.random.seed(11)
n = 2000
WOB = np.random.uniform(10, 45, n)
RPM = np.random.uniform(60, 180, n)
ROP = (5 + 1.2 * WOB + 0.15 * RPM
- 0.01 * (WOB - 35) ** 2 - 0.0008 * (RPM - 150) ** 2
+ np.random.normal(0, 3, n))
return pd.DataFrame({"WOB": WOB, "RPM": RPM, "ROP": ROP})
interval = make_interval()
# ── Your work starts here ────────────────────────────────────────────────────
def rop_grid(df, wob_bins=WOB_BINS, rpm_bins=RPM_BINS):
"""Mean ROP per (WOB bin, RPM bin) cell as a 2-D table.
Rows are WOB bins, columns are RPM bins, values are mean ROP.
"""
d = df.copy()
d["WOB_bin"] = pd.cut(d["WOB"], bins=wob_bins)
d["RPM_bin"] = pd.cut(d["RPM"], bins=rpm_bins)
grid = (d.groupby(["WOB_bin", "RPM_bin"], observed=False)["ROP"]
.mean()
.unstack())
return grid
def optimal_wob_rpm(df, wob_bins=WOB_BINS, rpm_bins=RPM_BINS):
"""Return (best_wob, best_rpm, best_rop) for the highest-mean-ROP cell.
best_wob, best_rpm are pandas Interval objects; best_rop is a float.
"""
grid = rop_grid(df, wob_bins, rpm_bins)
stacked = grid.stack()
best_wob, best_rpm = stacked.idxmax()
best_rop = float(stacked.max())
return best_wob, best_rpm, best_rop
grid = rop_grid(interval)
best_wob, best_rpm, best_rop = optimal_wob_rpm(interval)
print("grid shape:", grid.shape)
print("best WOB bin:", best_wob, " best RPM bin:", best_rpm)
print("best mean ROP (ft/hr):", best_rop)
lockCopying code is a Full Access feature.