Exercise 8.9
Heavy-Oil Dead Viscosity vs API
A new exploration block on the western fringe of OML 58 keeps logging crude that is far heavier than the 32 API base reservoir. Before the asset team commits to a development concept, they need to know how the dead-oil viscosity of these crudes behaves across the API range they keep encountering, because viscosity, more than almost anything else, decides whether a barrel flows to surface under its own pressure or needs heat, dilution, or artificial lift.
You'll use the Beggs & Robinson dead-oil correlation at a single reservoir temperature of T_F = 150 degrees F:
Y = 10 ** (3.0324 - 0.02023 * API)
X = Y * T_F ** (-1.163)
mu_od = 10 ** X - 1 # dead-oil viscosity, cpmu_od is the viscosity of the oil with no dissolved gas, the worst case, which is exactly what matters when you are screening cold, gas-starved heavy crudes.
Write two functions:
dead_visc_by_api(api_list, T_F): return a dict mapping each API in
api_list to its Beggs-Robinson dead-oil viscosity mu_od (cp) at T_F. Run it over the standard screening fan API = [10, 15, 20, 25, 30, 35, 40] and store the result in visc_by_api.
first_api_below(threshold_cp, api_list, T_F): return the lowest API in
api_list whose mu_od is still under threshold_cp. This is the "heaviest crude we can still flow" answer: anything below that API is too viscous. If no API clears the threshold, return None.
The asset team uses a 50 cp cold-flow screening limit for primary production, so store pumpable_api = first_api_below(50.0, API_LIST, T_F). For context, the industry's hard 100 cp practicality line is where a crude stops being a candidate for unassisted primary recovery at all; at 150 F the 10 API tar on this block sits right against that 100 cp wall, while the 40 API condensate-like end is barely a few cp.
Optionally, plot mu_od versus API to see how violently viscosity collapses as the oil gets lighter (a log y-axis makes the curve readable).
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
# Reservoir temperature for the western OML 58 screening fan (deg F).
T_F = 150.0
# Standard API screening fan: 10 (tar) up to 40 (light).
API_LIST = [10, 15, 20, 25, 30, 35, 40]
# Cold-flow primary-production screening limit (cp).
COLD_FLOW_LIMIT_CP = 50.0
def beggs_robinson_dead_oil(T_F, API):
"""Beggs & Robinson dead-oil viscosity mu_od (cp): no dissolved gas."""
Y = 10 ** (3.0324 - 0.02023 * API)
X = Y * T_F ** (-1.163)
return 10 ** X - 1
def dead_visc_by_api(api_list, T_F):
"""Return {API: mu_od_cp} for each API in api_list at temperature T_F."""
return {api: float(beggs_robinson_dead_oil(T_F, api)) for api in api_list}
def first_api_below(threshold_cp, api_list, T_F):
"""Lowest API whose mu_od < threshold_cp; None if none clear it."""
for api in sorted(api_list):
if beggs_robinson_dead_oil(T_F, api) < threshold_cp:
return api
return None
visc_by_api = dead_visc_by_api(API_LIST, T_F)
pumpable_api = first_api_below(COLD_FLOW_LIMIT_CP, API_LIST, T_F)
print("dead-oil viscosity by API (cp):", visc_by_api)
print("heaviest crude under 50 cp limit -> API:", pumpable_api)
lockCopying code is a Full Access feature.