examples.buildup
Primary depletion, then pressure buildup after shut-in -- a "well test". In metric units.
The producer flows for a while, and is then shut in (rate set to 0). The pressure then builds up, asymptotically towards the average pressure, which is now constant since nothing enters or leaves the (closed) reservoir.
Everything here is a consequence of ct > 0:
- The flow period is primary depletion: production without injection.
Impossible with
ct = 0(the rates must balance, refWells.rates); withct > 0the deficit is drawn from storage, i.e. from the expansion of the rock and fluids as the pressure drops -- at exactly the rate that material balance dictates, $ dp̄/dt = -q / (c_t V_p) $ ($V_p$ = pore volume; asserted below). - Monitor points further from the well respond later (and, after shut-in, keep declining for a while before turning around: they have not yet "heard" that the well was shut).
- The relaxation is gradual, i.e. it has a memory of the flow history -- this is what makes well testing a viable inference method.
For contrast, we also run the incompressible model -- where a shut-in is
felt everywhere immediately, and completely: the pressure (which is then only
defined up to a constant, ref examples.pressure_diffusion) instantly
becomes uniform. NB: since ct = 0 demands balanced rates, that run needs an
active injector, whose rate is switched off at the same time.
No water is present, so S = 0 throughout -- effectively a single-phase
example, the lone producer being (rates being signed) the only well there is.
Every pressure plotted here is a cell pressure, not a wellbore one: for the
latter, give the well an rw, ref examples.well_control.
Units
Unlike the other examples -- which leave the units unspecified (default)
this one is posed in practical metric units: metres, days, bar, mD, cP,
by changing ResSim.cdarcy to $ C = 0.008527 $.
Moreover, a 2 km square of 100 mD rock at 250 bar, produced at 20 m³/day/m, has a pressure diffusivity of
$$ η = C K λ / (φ c_t) ≈ 4.3 \times 10^4 \; \mathrm{m^2/day} \,, $$
so the well feels the boundary, 1 km away, after some $ r^2/η ≈ 23 $ days -- whence the 12-day flow period and 28-day buildup simulated here.
It also lets the run be interpreted the way a real well test is. During radial transient flow the line-source solution gives
$$ p_i - p = \frac{q μ}{4 π C K} \left[ \ln \frac{4 η t}{e^γ r^2} \right] \,, $$
so the semilog derivative $ dp / d\ln t $ plateaus at $ q μ / (4 π C K) $ --
the classic diagnostic of pressure-transient analysis. Being independent of
$ r $, the cell pressure will do (no well model needed), and reading $ K $ off
that plateau recovers the 100 mD that went in, to within 4%. The shortfall is
the time discretization, not the units: at dt = .05 it is 1%.
Nothing here knows that the numbers are metric.
The axis labels below say "[bar]" because this script says so.
cdarcy fixes the arithmetic, not the nomenclature.
In the figures:
- "time series" (left): the delay of the response with distance -- by the time the well (r = 0) has dropped by 12 bar, the r = 1000 m point has barely moved. After the shut-in the near-well pressure recovers at once, whereas the distant points keep declining for a while before turning around: they have not yet heard of it. All then converge on $\bar{p}$ (dashed), which falls along the material-balance line while the well flows, and is constant after.
- "time series" (middle): for
ct > 0the drawdown decays smoothly over the remainder of the run. Forct = 0it is a rectangle: rate on, rate off, and nothing in between. - "time series" (right): the semilog derivative, and the plateau read off its minimum. It is a shallow minimum rather than a flat stretch, being squeezed from both sides: at early times the cell average has not yet resolved the transient, and at late times (dotted) the closed boundary ends the radial regime. Hence the 4%.
- "pressure": the depression cone, growing while the well flows (the first two panels), then filling in -- the sharp, near-well part first, the broad remainder last. The colour scale is shared.
1"""Primary depletion, then pressure buildup after shut-in -- a "well test". **In metric units.** 2 3The producer flows for a while, and is then shut in (rate set to 0). 4The pressure then *builds up*, asymptotically towards the average pressure, 5which is now constant since nothing enters or leaves the (closed) reservoir. 6 7Everything here is a consequence of `ct > 0`: 8 9- The flow period is *primary depletion*: production **without injection**. 10 Impossible with `ct = 0` (the rates must balance, ref `Wells.rates`); with 11 `ct > 0` the deficit is drawn from *storage*, i.e. from the expansion of the 12 rock and fluids as the pressure drops -- at exactly the rate that material 13 balance dictates, $ dp̄/dt = -q / (c_t V_p) $ ($V_p$ = pore volume; asserted below). 14- Monitor points further from the well respond later 15 (and, after shut-in, keep declining for a while before turning around: 16 they have not yet "heard" that the well was shut). 17- The relaxation is gradual, i.e. it has a memory of the flow history -- 18 this is what makes well testing a viable inference method. 19 20For contrast, we also run the incompressible model -- where a shut-in is 21felt everywhere immediately, and completely: the pressure (which is then only 22defined up to a constant, ref `examples.pressure_diffusion`) instantly 23becomes uniform. NB: since `ct = 0` demands balanced rates, that run needs an 24active injector, whose rate is switched off at the same time. 25 26No water is present, so `S = 0` throughout -- effectively a single-phase 27example, the lone producer being (rates being signed) the only well there is. 28Every pressure plotted here is a *cell* pressure, not a wellbore one: for the 29latter, give the well an `rw`, ref `examples.well_control`. 30 31## Units 32 33Unlike the other examples -- which leave the units unspecified (default) 34this one is posed in practical **metric** units: metres, days, bar, mD, cP, 35by changing `ResSim.cdarcy` to $ C = 0.008527 $. 36 37Moreover, a 2 km square of 100 mD rock 38at 250 bar, produced at 20 m³/day/m, has a pressure diffusivity of 39 40$$ η = C K λ / (φ c_t) ≈ 4.3 \\times 10^4 \\; \\mathrm{m^2/day} \\,, $$ 41 42so the well feels the boundary, 1 km away, after some $ r^2/η ≈ 23 $ days -- 43whence the 12-day flow period and 28-day buildup simulated here. 44 45It also lets the run be *interpreted* the way a real well test is. During 46radial transient flow the line-source solution gives 47 48$$ p_i - p = \\frac{q μ}{4 π C K} \\left[ \\ln \\frac{4 η t}{e^γ r^2} \\right] \\,, $$ 49 50so the semilog derivative $ dp / d\\ln t $ **plateaus** at $ q μ / (4 π C K) $ -- 51the classic diagnostic of pressure-transient analysis. Being independent of 52$ r $, the *cell* pressure will do (no well model needed), and reading $ K $ off 53that plateau recovers the 100 mD that went in, to within 4%. The shortfall is 54the time discretization, not the units: at `dt = .05` it is 1%. 55 56.. note:: Nothing here knows that the numbers are metric. 57 58 The axis labels below say "[bar]" because *this script* says so. 59 `cdarcy` fixes the arithmetic, not the nomenclature. 60 61In the figures: 62 63- "time series" (left): the delay of the response with distance -- by the time 64 the well (r = 0) has dropped by 12 bar, the r = 1000 m point has barely moved. 65 After the shut-in the near-well pressure recovers at once, whereas the 66 distant points keep *declining* for a while before turning around: they have 67 not yet heard of it. All then converge on $\\bar{p}$ (dashed), which falls 68 along the material-balance line while the well flows, and is constant after. 69- "time series" (middle): for `ct > 0` the drawdown decays smoothly over the 70 remainder of the run. For `ct = 0` it is a rectangle: rate on, rate off, 71 and nothing in between. 72- "time series" (right): the semilog derivative, and the plateau read off its 73 minimum. It is a *shallow* minimum rather than a flat stretch, being squeezed 74 from both sides: at early times the cell average has not yet resolved the 75 transient, and at late times (dotted) the closed boundary ends the radial 76 regime. Hence the 4%. 77- "pressure": the depression cone, growing while the well flows (the first two 78 panels), then filling in -- the sharp, near-well part first, the broad 79 remainder last. The colour scale is shared. 80""" 81 82from mpl_tools.place import freshfig 83import numpy as np 84 85from minires import ResSim 86from minires.plotting import show 87 88## Setup -- a 2 km square of 100 mD rock at 250 bar 89L = 2000 # m 90N = 64 91q = 20 # m²/day, i.e. m³/day per metre of thickness 92perm = 100 # mD 93mu = 1 # cP 94por = .2 95ct = 1e-4 # 1/bar 96p_i = 250 # bar 97dt = .2 # day 98nSteps = 200 99kShut = 60 # Time index of shut-in (t = 12 day) 100tt = dt*np.arange(nSteps + 1) 101 102schedule = np.where(np.arange(nSteps) < kShut, q, 0) 103# Aside: feedback control (e.g. shut-in upon water breakthrough) would instead 104# be implemented by overriding `ResSim.well_controls`. 105 106# `cdarcy` for m/day/bar/mD/cP: the darcy itself (9.869233e-16 m²), 107# expressed in the system -- i.e. 0.008527. Ref `ResSim.cdarcy`. 108C = 86400 * 9.869233e-16 * 1e5 / 1e-3 109 110grid: dict = dict(Lx=L, Ly=L, Nx=N, Ny=N, cdarcy=C, 111 K=perm, por=por*np.ones((N, N)), fluid=dict(vw=mu, vo=mu)) # fmt: skip 112 113model = ResSim(**grid, ct=ct, 114 wells=[dict(name="P1", xy=[L/2, L/2], rate=-schedule)]) # fmt: skip 115 116# Incompressible analogue: the injector must match the producer at all times. 117model_inc = ResSim(**grid, 118 wells=[dict(name="I1", xy=[0, 0], rate=+schedule), 119 dict(name="P1", xy=[L/2, L/2], rate=-schedule)]) # fmt: skip 120 121oil_only = np.zeros(model.Nxy) 122P0 = np.full(model.Nxy, p_i) 123 124eta = C*perm/mu/(por*ct) # Diffusivity (λ = 1, there being no water) 125unit_p, unit_t = " [bar]", " [day]" 126 127## Simulate 128SS, PP = model.sim(dt, nSteps, oil_only, P0=P0, pbar=False) 129_ , PP_inc = model_inc.sim(dt, nSteps, oil_only, pbar=False) 130 131iw = model.xy2ind(*model.wells.xy[0]) 132p_mean = PP.mean(axis=1) 133p_cell = PP[:, iw] 134 135## Well test: the permeability, read off the semilog derivative's plateau 136kk = np.arange(2, kShut) # NB: skip t = 0, whose log is -inf 137dp_dlnt = -(p_cell[kk+1] - p_cell[kk-1]) / (np.log(tt[kk+1]) - np.log(tt[kk-1])) 138plateau = dp_dlnt.min() 139perm_est = q*mu / (4*np.pi*C*plateau) 140 141## Plot: monitor points, the drawdown, and the well test 142fig, (ax1, ax2, ax3) = freshfig("Buildup -- time series", ncols=3, figsize=(14, 4)) 143 144for r in [0, 200, 500, 1000]: 145 i = model.xy2ind(L/2 + r, L/2) 146 ax1.plot(tt, PP[:, i], label=f"r = {r} m") # r = 0 is the well's cell 147ax1.plot(tt, p_mean, "k--", lw=1, 148 label="Mean, $\\bar{p}$: $p_i - qt/(c_t V_p)$, then constant") 149ax1.axvline(kShut*dt, c="k", lw=1, alpha=.4) 150ax1.annotate("shut-in", (kShut*dt, PP.min()), fontsize="small", 151 xytext=(4, 0), textcoords="offset points") 152ax1.set(title="Pressure at increasing distance from the well", 153 xlabel=f"Time{unit_t}", ylabel=f"p{unit_p}") 154ax1.legend(fontsize="small") 155 156drawdown = p_mean - p_cell 157drawdown_inc = PP_inc.mean(axis=1) - PP_inc[:, iw] 158ax2.plot(tt[1:], drawdown[1:] , label="$c_t > 0$") 159ax2.plot(tt[1:], drawdown_inc[1:], label="$c_t = 0$") 160ax2.axvline(kShut*dt, c="k", lw=1, alpha=.4) 161ax2.set(title="Drawdown, $\\bar{p} - p_\\mathrm{cell}$", xlabel=f"Time{unit_t}", 162 ylabel=f"$\\Delta p${unit_p}") 163ax2.legend() 164 165ax3.plot(tt[kk], dp_dlnt, "-o", ms=3) 166ax3.axhline(plateau, c="k", ls="--", lw=1, 167 label=f"Plateau ⇒ K = {perm_est:.0f} mD") 168ax3.axvline((L/2)**2/eta, c="C2", ls=":", lw=1, label="$r^2/η$ (boundary)") 169ax3.set(title="Well test: $dp / d\\ln t$", xlabel=f"Time{unit_t}", xscale="log", 170 ylabel=f"$dp/d\\ln t${unit_p}") 171ax3.legend(fontsize="small") 172fig.tight_layout() 173 174## Plot: the depression cone growing, then filling in 175fig, axs = freshfig("Buildup -- pressure", ncols=5, sharex=True, sharey=True, 176 figsize=(13.5, 3.2)) 177kws: dict = dict(levels=np.linspace(PP.min(), p_i, 21), cmap="viridis", 178 colorbar=False, finalize=False, wells=dict(size=.4)) 179snapshots = [kShut // 6, kShut, kShut + 2, kShut + 10, nSteps] 180for i, (ax, k) in enumerate(zip(axs, snapshots)): 181 cc = model.plt_field(ax, PP[k], **kws, labels=(i == 0), 182 title=f"t = {k*dt:.1f} day" + (" (shut-in)" if k == kShut else "")) 183fig.colorbar(cc, ax=axs, shrink=.5, label=f"p{unit_p}") 184 185# While the well flows, the average pressure declines exactly as material 186# balance dictates (see also `tests/test_compressible.py`) ... 187Vp = L*L*por # pore volume (areal, like `q`) 188assert np.allclose(p_mean[:kShut + 1], p_i - q*tt[:kShut + 1]/(ct*Vp)) 189# ... and after shut-in it is constant (nothing enters or leaves) ... 190assert np.allclose(p_mean[kShut:], p_mean[kShut]) 191# ... and the pressure equilibrates towards it: by the end of the run, the 192# spread has decayed to less than 1% of what it was at shut-in. 193assert np.ptp(PP[-1]) < .01 * np.ptp(PP[kShut]) 194# Whereas the incompressible model forgets everything in a single step: 195assert np.allclose(PP_inc[kShut + 1:], 0) 196# The well test recovers the permeability that went in, to within 4% 197assert abs(perm_est/perm - 1) < .04 198 199# Regression values, checked by `tests/test_examples.py`. 200__digest__ = dict(p_cell = p_cell[::10], 201 p_far = PP[::10, model.xy2ind(L, L/2)], 202 p_mean = p_mean[::10], 203 p_final = PP[-1], 204 perm_est = perm_est) 205 206if __name__ == "__main__": 207 show()