Exerciseschevron_rightChapter 11chevron_right11.9
fitness_center

Exercise 11.9

Corey Exponent Sensitivity

Level 2
Chapter 11: Reservoir Simulation
descriptionProblem

Using the Buckley-Leverett analysis, investigate how the Corey exponents (non_o and nwn_w) affect the shape of the fractional flow curve and the shock front saturation. Test values from 1.5 to 4.0. What physical conditions lead to higher or lower Corey exponents?

---

Before a Buckley-Leverett study even starts, the relative-permeability curves decide everything downstream: the fractional-flow shape, the shock front, the sweep. The OML 58 reservoir team has good drainage kro data but is unsure about the water curvature: the special-core-analysis lab fit the water curve with a Corey exponent anywhere from nw = 2 to nw = 4, and the booking depends on which one you trust. A higher nw means water stays immobile longer as the rock is flooded: sharper curvature, less water relative permeability at any given saturation, which usually points to a more water-wet, finer-pored rock.

You will quantify that water-curvature sensitivity directly off the Corey model. The corey_relperm helper is embedded; do not re-derive it. The oil exponent is held fixed at no = 2.0; only nw varies.

The endpoints are fixed for OML 58: connate water SWI = 0.2, residual oil SOR = 0.2, kro_max = 0.8, krw_max = 0.3. The shared saturation grid SW runs from SWI to 1 - SOR.

Your tasks:

  1. Write kr_curves(nw_list) returning a dict mapping each nw to a

(kro, krw) tuple of numpy arrays evaluated on the shared SW grid with no=2.0 fixed: corey_relperm(SW, SWI, SOR, kro_max=KRO_MAX, krw_max=KRW_MAX, no=2.0, nw=nw).

  1. Call kr_curves([2, 3, 4]) and store it in curves_by_nw.
  1. Write plot_kr_families(nw_list) that draws, on one axes, the kro(Sw) and

krw(Sw) lines for every nw in nw_list (so at least 2 * len(nw_list) lines), with an x-label, a y-label, a title, and a legend. Return the Figure.

The grader checks the physics, not the styling: at any interior Sw a higher nw gives a lower krw (sharper curvature); the endpoints krw(SWI) = 0 and kro(1 - SOR) = 0 must hold exactly; and it recomputes krw at one saturation for one nw from the Corey definition. It also confirms krw reaches its endpoint KRW_MAX at Sw = 1 - SOR for every nw.

> Think about it: every krw curve passes through the same two > endpoints: krw = 0 at SWI and krw = KRW_MAX at 1 - SOR. The exponent > only bends the curve between them. Why does a sharper (higher-nw) water > curve usually delay water breakthrough and improve sweep?

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
import matplotlib.pyplot as plt


# ── Embedded Corey relative-permeability helper (do not edit) ────────────
def corey_relperm(Sw, Swi, Sor, kro_max=0.8, krw_max=0.3, no=2.0, nw=3.0):
    Sn = np.clip((Sw - Swi) / (1 - Swi - Sor), 0, 1)
    return kro_max * (1 - Sn) ** no, krw_max * Sn ** nw


# ── OML 58 SCAL endpoints + shared saturation grid ──────────────────────
SWI = 0.2          # connate water saturation (sw, v/v)
SOR = 0.2          # residual oil saturation (sw, v/v)
KRO_MAX = 0.8      # oil endpoint relative permeability
KRW_MAX = 0.3      # water endpoint relative permeability
NO = 2.0           # oil Corey exponent (held fixed)
NPTS = 401
SW = np.linspace(SWI, 1 - SOR, NPTS)   # shared Sw grid (sw, v/v)
NW_LIST = [2, 3, 4]                    # water Corey exponents to compare


def kr_curves(nw_list):
    """Dict mapping each nw -> (kro, krw) arrays on the shared SW grid.

    Oil exponent is fixed at no=NO; only the water exponent nw varies.
    """
    return {
        nw: corey_relperm(SW, SWI, SOR, kro_max=KRO_MAX, krw_max=KRW_MAX,
                          no=NO, nw=nw)
        for nw in nw_list
    }


curves_by_nw = kr_curves(NW_LIST)


def plot_kr_families(nw_list):
    """Plot kro(Sw) and krw(Sw) for every nw on one axes; return the Figure."""
    fig, ax = plt.subplots(figsize=(7, 5))
    curves = kr_curves(nw_list)
    for nw in nw_list:
        kro, krw = curves[nw]
        ax.plot(SW, kro, lw=1.6, ls="--", label=f"kro (no={NO:g})" if nw == nw_list[0] else None)
        ax.plot(SW, krw, lw=1.8, label=f"krw (nw={nw})")
    ax.set_xlabel("Water saturation Sw (v/v)")
    ax.set_ylabel("Relative permeability kr")
    ax.set_title("OML 58 Corey curves: water-exponent sensitivity")
    ax.legend(loc="upper center")
    ax.grid(True, alpha=0.25)
    return fig


fig = plot_kr_families(NW_LIST)
print("krw at Sw=1-Sor by nw:",
      {nw: round(float(curves_by_nw[nw][1][-1]), 4) for nw in NW_LIST})
plt.show()

lockCopying code is a Full Access feature.