Buckley-Leverett two-phase problem
Introduction Validation ImmiscibleThe Buckley-Leverett test problem is a classical reservoir simulation benchmark that demonstrates the nonlinear displacement process of a viscous fluid being displaced by a less viscous fluid, typically taken to be water displacing oil.
Problem definition
This is a simple model without wells, where the flow is driven by a simple source term and a simple constant pressure boundary condition at the outlet. We define a function that sets up a two-phase system, a simple 1D domain and replaces the default relative permeability functions with quadratic functions:
In addition, the phase viscosities are treated as constant parameters of 1 and 5 centipoise for the displacing and resident fluids, respectively.
The function is parametrized on the number of cells and the number of time-steps used to solve the model. This function, since it uses a relatively simple setup without wells, uses the Jutul functions directly.
using JutulDarcy, Jutul
function solve_bl(;nc = 100, time = 1.0, nstep = nc)
T = time
tstep = repeat([T/nstep], nstep)
domain = get_1d_reservoir(nc)
nc = number_of_cells(domain)
timesteps = tstep*3600*24
bar = 1e5
p0 = 100*bar
sys = ImmiscibleSystem((LiquidPhase(), VaporPhase()))
model = SimulationModel(domain, sys)
kr = BrooksCoreyRelativePermeabilities(sys, [2.0, 2.0], [0.2, 0.2])
replace_variables!(model, RelativePermeabilities = kr)
tot_time = sum(timesteps)
pv = pore_volume(domain)
irate = 500*sum(pv)/tot_time
src = SourceTerm(1, irate, fractional_flow = [1.0, 0.0])
bc = FlowBoundaryCondition(nc, p0/2)
forces = setup_forces(model, sources = src, bc = bc)
parameters = setup_parameters(model, PhaseViscosities = [1e-3, 5e-3]) # 1 and 5 cP
state0 = setup_state(model, Pressure = p0, Saturations = [0.0, 1.0])
states, report = simulate(state0, model, timesteps,
forces = forces, parameters = parameters)
return states, model, report
endsolve_bl (generic function with 1 method)Run the base case
We solve a small model with 100 cells and 100 steps to serve as the baseline.
n, n_f = 100, 1000
states, model, report = solve_bl(nc = n)
print_stats(report)Jutul: Simulating 1 day as 100 report steps
╭────────────────┬───────────┬───────────────┬──────────╮
│ Iteration type │ Avg/step │ Avg/ministep │ Total │
│ │ 100 steps │ 100 ministeps │ (wasted) │
├────────────────┼───────────┼───────────────┼──────────┤
│ Newton │ 4.15 │ 4.15 │ 415 (0) │
│ Linearization │ 5.15 │ 5.15 │ 515 (0) │
│ Linear solver │ 4.15 │ 4.15 │ 415 (0) │
│ Precond apply │ 0.0 │ 0.0 │ 0 (0) │
╰────────────────┴───────────┴───────────────┴──────────╯
╭───────────────┬────────┬────────────┬────────╮
│ Timing type │ Each │ Relative │ Total │
│ │ ms │ Percentage │ s │
├───────────────┼────────┼────────────┼────────┤
│ Properties │ 0.0137 │ 0.21 % │ 0.0057 │
│ Equations │ 0.4525 │ 8.59 % │ 0.2330 │
│ Assembly │ 0.2160 │ 4.10 % │ 0.1112 │
│ Linear solve │ 2.4342 │ 37.23 % │ 1.0102 │
│ Linear setup │ 0.0000 │ 0.00 % │ 0.0000 │
│ Precond apply │ 0.0000 │ 0.00 % │ 0.0000 │
│ Update │ 0.0136 │ 0.21 % │ 0.0056 │
│ Convergence │ 0.1807 │ 3.43 % │ 0.0930 │
│ Input/Output │ 0.3025 │ 1.11 % │ 0.0302 │
│ Other │ 2.9503 │ 45.12 % │ 1.2244 │
├───────────────┼────────┼────────────┼────────┤
│ Total │ 6.5383 │ 100.00 % │ 2.7134 │
╰───────────────┴────────┴────────────┴────────╯
╭────────────────┬───────────┬───────────────┬──────────╮
│ Iteration type │ Avg/step │ Avg/ministep │ Total │
│ │ 100 steps │ 100 ministeps │ (wasted) │
├────────────────┼───────────┼───────────────┼──────────┤
│ Newton │ 4.15 │ 4.15 │ 415 (0) │
│ Linearization │ 5.15 │ 5.15 │ 515 (0) │
│ Linear solver │ 4.15 │ 4.15 │ 415 (0) │
│ Precond apply │ 0.0 │ 0.0 │ 0 (0) │
╰────────────────┴───────────┴───────────────┴──────────╯
╭───────────────┬────────┬────────────┬────────╮
│ Timing type │ Each │ Relative │ Total │
│ │ ms │ Percentage │ s │
├───────────────┼────────┼────────────┼────────┤
│ Properties │ 0.0137 │ 0.21 % │ 0.0057 │
│ Equations │ 0.4525 │ 8.59 % │ 0.2330 │
│ Assembly │ 0.2160 │ 4.10 % │ 0.1112 │
│ Linear solve │ 2.4342 │ 37.23 % │ 1.0102 │
│ Linear setup │ 0.0000 │ 0.00 % │ 0.0000 │
│ Precond apply │ 0.0000 │ 0.00 % │ 0.0000 │
│ Update │ 0.0136 │ 0.21 % │ 0.0056 │
│ Convergence │ 0.1807 │ 3.43 % │ 0.0930 │
│ Input/Output │ 0.3025 │ 1.11 % │ 0.0302 │
│ Other │ 2.9503 │ 45.12 % │ 1.2244 │
├───────────────┼────────┼────────────┼────────┤
│ Total │ 6.5383 │ 100.00 % │ 2.7134 │
╰───────────────┴────────┴────────────┴────────╯Run refined version (1000 cells, 1000 steps)
Using a grid with 100 cells will not yield a fully converged solution. We can increase the number of cells at the cost of increasing the runtime a bit. Note that most of the time is spent in the linear solver, which uses a direct sparse LU factorization by default. For larger problems it is recommended to use an iterative solver. The high-level interface used in later examples automatically sets up an iterative solver with the appropriate preconditioner.
states_refined, _, report_refined = solve_bl(nc = n_f);
print_stats(report_refined)Jutul: Simulating 23 hours, 60 minutes as 1000 report steps
╭────────────────┬────────────┬────────────────┬──────────╮
│ Iteration type │ Avg/step │ Avg/ministep │ Total │
│ │ 1000 steps │ 1000 ministeps │ (wasted) │
├────────────────┼────────────┼────────────────┼──────────┤
│ Newton │ 4.12 │ 4.12 │ 4120 (0) │
│ Linearization │ 5.12 │ 5.12 │ 5120 (0) │
│ Linear solver │ 4.12 │ 4.12 │ 4120 (0) │
│ Precond apply │ 0.0 │ 0.0 │ 0 (0) │
╰────────────────┴────────────┴────────────────┴──────────╯
╭───────────────┬────────┬────────────┬─────────╮
│ Timing type │ Each │ Relative │ Total │
│ │ ms │ Percentage │ s │
├───────────────┼────────┼────────────┼─────────┤
│ Properties │ 0.0790 │ 2.88 % │ 0.3253 │
│ Equations │ 0.0711 │ 3.23 % │ 0.3639 │
│ Assembly │ 0.0654 │ 2.97 % │ 0.3348 │
│ Linear solve │ 2.3966 │ 87.53 % │ 9.8740 │
│ Linear setup │ 0.0000 │ 0.00 % │ 0.0000 │
│ Precond apply │ 0.0000 │ 0.00 % │ 0.0000 │
│ Update │ 0.0274 │ 1.00 % │ 0.1131 │
│ Convergence │ 0.0260 │ 1.18 % │ 0.1332 │
│ Input/Output │ 0.0229 │ 0.20 % │ 0.0229 │
│ Other │ 0.0276 │ 1.01 % │ 0.1139 │
├───────────────┼────────┼────────────┼─────────┤
│ Total │ 2.7381 │ 100.00 % │ 11.2811 │
╰───────────────┴────────┴────────────┴─────────╯
╭────────────────┬────────────┬────────────────┬──────────╮
│ Iteration type │ Avg/step │ Avg/ministep │ Total │
│ │ 1000 steps │ 1000 ministeps │ (wasted) │
├────────────────┼────────────┼────────────────┼──────────┤
│ Newton │ 4.12 │ 4.12 │ 4120 (0) │
│ Linearization │ 5.12 │ 5.12 │ 5120 (0) │
│ Linear solver │ 4.12 │ 4.12 │ 4120 (0) │
│ Precond apply │ 0.0 │ 0.0 │ 0 (0) │
╰────────────────┴────────────┴────────────────┴──────────╯
╭───────────────┬────────┬────────────┬─────────╮
│ Timing type │ Each │ Relative │ Total │
│ │ ms │ Percentage │ s │
├───────────────┼────────┼────────────┼─────────┤
│ Properties │ 0.0790 │ 2.88 % │ 0.3253 │
│ Equations │ 0.0711 │ 3.23 % │ 0.3639 │
│ Assembly │ 0.0654 │ 2.97 % │ 0.3348 │
│ Linear solve │ 2.3966 │ 87.53 % │ 9.8740 │
│ Linear setup │ 0.0000 │ 0.00 % │ 0.0000 │
│ Precond apply │ 0.0000 │ 0.00 % │ 0.0000 │
│ Update │ 0.0274 │ 1.00 % │ 0.1131 │
│ Convergence │ 0.0260 │ 1.18 % │ 0.1332 │
│ Input/Output │ 0.0229 │ 0.20 % │ 0.0229 │
│ Other │ 0.0276 │ 1.01 % │ 0.1139 │
├───────────────┼────────┼────────────┼─────────┤
│ Total │ 2.7381 │ 100.00 % │ 11.2811 │
╰───────────────┴────────┴────────────┴─────────╯Plot results
We plot the saturation front for the base case at different times together with the final solution for the refined model. In this case, refining the grid by a factor 10 gave us significantly less smearing of the trailing front.
using GLMakie
x = range(0, stop = 1, length = n)
x_f = range(0, stop = 1, length = n_f)
f = Figure()
ax = Axis(f[1, 1], ylabel = "Saturation", title = "Buckley-Leverett displacement")
for i in 1:6:length(states)
lines!(ax, x, states[i][:Saturations][1, :], color = :darkgray)
end
lines!(ax, x_f, states_refined[end][:Saturations][1, :], color = :red)
f
Example on GitHub
If you would like to run this example yourself, it can be downloaded from the JutulDarcy.jl GitHub repository as a script, or as a Jupyter Notebook
This example took 19.424491283 seconds to complete.This page was generated using Literate.jl.