Exerciseschevron_rightChapter 8chevron_right8.9
fitness_center

Exercise 8.9

Heavy-Oil Dead Viscosity vs API

Level 2
Chapter 8: PVT Correlations
descriptionProblem

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, cp

mu_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).

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

# 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.