Exerciseschevron_rightChapter 5chevron_right5.2
fitness_center

Exercise 5.2

Waterflood Surveillance Dashboard

Level 3
Chapter 5: Data Visualization & Plotting
descriptionProblem

Build a 2×2 surveillance dashboard for a waterflood project. Synthesise the underlying data and plot all four panels in a single figure built with fig, axes = plt.subplots(2, 2, figsize=(14, 9)).

Use 36 monthly time-points for everything (t = np.arange(0, 36)). Build the data however you like (exponential decline, sigmoid water cut, etc.); the grader only checks plot structure, not the exact synthetic values.

Panels (build them in this order):

  1. axes[0, 0]: oil + water rate, dual y-axis. Plot oil rate on the

primary y-axis, water rate on a secondary axis created with axes[0, 0].twinx(). The chart must have at least 2 lines.

  1. axes[0, 1]: water cut over time with a horizontal reference

line at the 0.90 economic limit (axhline).

  1. axes[1, 0]: cumulative oil vs cumulative water injected. Use

np.cumsum to build the two cumulative series; plot the second against the first.

  1. axes[1, 1]: injection rate over time for 3 injection wells.

Three lines on one axis.

Add a fig.suptitle(...) for the dashboard title. Each panel needs axis labels and (where multiple lines are shown) a legend.

> Think about it: in panel 3, what does the slope of cum-oil vs > cum-water-injected tell you about sweep efficiency? What happens to > that slope as the flood matures?

lightbulbHints (0/5)

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

t = np.arange(0, 36)

# Synthetic data
oil_rate    = 1500 * np.exp(-0.04 * t)
water_rate  = 200 + 50 * t + 30 * np.sin(t / 3)
water_cut   = water_rate / (oil_rate + water_rate)

cum_oil       = np.cumsum(oil_rate * 30)        # bbl
cum_water_inj = np.cumsum((1800 + 80 * t) * 30) # bbl

inj_a = 1800 + 80 * t  + 50 * np.sin(t / 4)
inj_b = 1500 + 60 * t  + 30 * np.cos(t / 5)
inj_c = 2000 + 40 * t  + 70 * np.sin(t / 6 + 1)

fig, axes = plt.subplots(2, 2, figsize=(14, 9))

# Panel 1: dual y-axis
ax1 = axes[0, 0]
ax1.plot(t, oil_rate, "C2-", label="Oil rate (bopd)")
ax1.set_xlabel("Month")
ax1.set_ylabel("Oil rate (bopd)", color="C2")
ax1b = ax1.twinx()
ax1b.plot(t, water_rate, "C0-", label="Water rate (bwpd)")
ax1b.set_ylabel("Water rate (bwpd)", color="C0")
ax1.set_title("Oil & water rate")

# Panel 2: water cut with economic limit
ax2 = axes[0, 1]
ax2.plot(t, water_cut, "C3-", label="Water cut")
ax2.axhline(0.90, color="black", linestyle="--", label="Economic limit (0.90)")
ax2.set_xlabel("Month"); ax2.set_ylabel("Water cut")
ax2.set_ylim(0, 1)
ax2.set_title("Water cut over time")
ax2.legend()

# Panel 3: cum_oil vs cum_water_inj
ax3 = axes[1, 0]
ax3.plot(cum_water_inj / 1e6, cum_oil / 1e6, "C1-")
ax3.set_xlabel("Cumulative water injected (MMbbl)")
ax3.set_ylabel("Cumulative oil produced (MMbbl)")
ax3.set_title("Sweep efficiency: cum oil vs cum water injected")
ax3.grid(True, alpha=0.3)

# Panel 4: three injectors
ax4 = axes[1, 1]
ax4.plot(t, inj_a, label="INJ-A")
ax4.plot(t, inj_b, label="INJ-B")
ax4.plot(t, inj_c, label="INJ-C")
ax4.set_xlabel("Month"); ax4.set_ylabel("Injection rate (bwpd)")
ax4.set_title("Injection rates by well")
ax4.legend()

fig.suptitle("Waterflood Surveillance Dashboard", fontweight="bold")
plt.tight_layout()
plt.show()

lockCopying code is a Full Access feature.