Exerciseschevron_rightChapter 11chevron_right11.4
fitness_center

Exercise 11.4

The CFL Condition - Why Explicit Time-Stepping Pays the Price

Level 2
Chapter 11: Reservoir Simulation
descriptionProblem

Implement the explicit time-stepping method. Calculate the CFL limit for the reservoir parameters in this chapter. Verify by running the explicit method with a time step just below and just above the CFL limit. Show that the solution diverges above the limit.

---

The implicit simulator you have been using (the embedded Reservoir1D) is unconditionally stable: you can march it forward with a 30-day step and it never blows up. An explicit scheme is cheaper per step (no linear solve) but it is only stable while the time step stays under the CFL limit. Push past that limit by even a hair and the pressure solution oscillates and diverges to garbage. This exercise quantifies exactly how punishing that constraint is.

For the diffusion problem solved in this chapter, the explicit stability ceiling in days is

ΔtCFL  =  ϕμctΔx22k(6.328×103)\Delta t_{\text{CFL}} \;=\; \frac{\phi\,\mu\,c_t\,\Delta x^{2}}{2\,k\,(6.328\times 10^{-3})}

Two things make it brutal. The grid spacing enters as Δx2\Delta x^{2}, so halving the cells quarters the allowed step. And it is inversely proportional to permeability, so a more permeable rock forces smaller steps. Either way, a real field grid drives ΔtCFL\Delta t_{\text{CFL}} down to a tiny fraction of a day, meaning thousands of explicit steps to cover a single year, versus the few hundred an implicit scheme can take with a 1-day step. That trade is the whole reason production simulators are implicit.

The chapter case (OML 58, the OD field): phi = 0.2, mu_cp = 2.0, ct_psi = 1.5e-5, dx_ft = 100.0, k_md = 100.0.

Your tasks:

  1. Write cfl_limit(phi, mu, ct, dx, k) returning the CFL time-step limit in

days using the formula above (the constant 6.328e-3 is already provided as ALPHA).

  1. Write steps_per_year_explicit(phi, mu, ct, dx, k) returning the number of

explicit steps needed to cover one year: ceil(365.0 / cfl_limit(...)) as an int. (One year is DAYS_PER_YEAR = 365.0; use math.ceil.)

  1. Compute and store:
  • dt_cfl_days = cfl_limit(...) for the chapter case (≈ 0.047 days).
  • n_explicit = steps_per_year_explicit(...) for the chapter case (a big

number (thousands).

  • n_implicit = 365: the steps an implicit run takes at dt = 1 day.

> Think about it: the explicit scheme needs ~n_explicit steps to do what > the implicit scheme does in 365. Now imagine refining dx from 100 ft to > 25 ft for a sharper flood front: the CFL step drops by a factor of 16, and the > explicit step count climbs by the same factor. Unconditional stability is not > a luxury; it is what makes field-scale simulation tractable at all.

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 math

# ── CFL constants for the explicit diffusion scheme ──────────────────────
# The 6.328e-3 factor pairs with the implicit simulator's beta = 1.127e-3:
# it is the diffusivity constant in field units (md, cp, 1/psi, ft, days).
ALPHA = 6.328e-3        # field-unit diffusivity constant
DAYS_PER_YEAR = 365.0   # days the explicit scheme must cover in one year

# ── Chapter case: OML 58, the OD field ───────────────────────────────────
PHI = 0.2          # porosity (fraction)
MU_CP = 2.0        # oil viscosity (cp)
CT_PSI = 1.5e-5    # total compressibility (1/psi)
DX_FT = 100.0      # grid-cell length (ft)
K_MD = 100.0       # permeability (md)


def cfl_limit(phi, mu, ct, dx, k):
    """Explicit-scheme CFL time-step limit, in DAYS.

    dt_cfl = phi * mu * ct * dx**2 / (2 * k * ALPHA)

    Smaller cells (dx**2) and higher permeability both SHRINK the limit.
    """
    return phi * mu * ct * dx ** 2 / (2.0 * k * ALPHA)


def steps_per_year_explicit(phi, mu, ct, dx, k):
    """Number of explicit steps needed to cover one year (DAYS_PER_YEAR).

    That is ceil(365.0 / cfl_limit(...)), returned as an int.
    """
    return math.ceil(DAYS_PER_YEAR / cfl_limit(phi, mu, ct, dx, k))


# ── Chapter-case results ─────────────────────────────────────────────────
dt_cfl_days = cfl_limit(PHI, MU_CP, CT_PSI, DX_FT, K_MD)
n_explicit = steps_per_year_explicit(PHI, MU_CP, CT_PSI, DX_FT, K_MD)
n_implicit = 365  # an implicit run at dt = 1 day covers a year in 365 steps

print("CFL limit (days):", dt_cfl_days)
print("explicit steps/year:", n_explicit, "vs implicit steps/year:", n_implicit)

lockCopying code is a Full Access feature.