Contents
Single phase flow simulation using AD
This example goes through the steps of setting up a single-phase simulation with a single horizontal well using the automatic differentiation framework.
require ad-fi % Setup 10x10x10 grid of 200x200x50 m model. nx = 10; ny = 10; nz = 10; Dx = 200; Dy = 200; Dz = 50; G = cartGrid([nx, ny, nz], [Dx, Dy, Dz]); G = computeGeometry(G); % Assume homogeneous/isotropic rock. permX = 30*milli*darcy; poro = 0.3; rock.perm = repmat(permX, [G.cells.num, 1]); rock.poro = repmat(poro , [G.cells.num, 1]); % Set rock compressibility: cr = 1e-6/barsa;
Rock properties
In the case of non-zero rock-compressiblity cr, the input rock porosity is taken as reference at a given reference pressure p_r. The grid pore volume (pv) becomes a function of pressure given by the differential equation cr = (d pv/d p)/pv which results in pv(p) = pv_r e^( cr(p-p_r) ) in which pv_r is the reference pore volume (rock.poro x volume) and p_r is the reference pressure. We assume the reference pressure is 200 Bar:
pv_r = poreVolume(G, rock);
p_r = 200*barsa;
% Finally, the pressure-dependent function for pore-volumes becomes:
pv = @(p) pv_r .* exp( cr .* (p - p_r) );
Fluid (oil) properties
Assume constant viscosity:
mu = 5*centi*poise; % Assume that the oil compressibility can be approximated as constant in % the reservoir: c = 1e-3/barsa; % When the compressibility is constant, the fluid density becomes a % function of pressure given by the differential equation % c = (d rho/d p)/rho % which results in % rho(p) = rho_r e^( c(p-p_r) ) % in which rho_r is the reference fluid density at reference pressure p_r. % We assume that rho_r = 850 kg/m^3 at p_r = 200 Bar: p_r = 200*barsa; rho_r = 850*kilogram/meter^3; % Finally define the pressure dependent function for rho: rho = @(p) rho_r .* exp( c .* (p - p_r) ); % Computing surface volume rates requires fluid density at surface % conditions. We assume it is 750 kg/m^3: rhoS = 750*kilogram/meter^3;
Single horizontal well in J direction
W = []; nperf = 8; I = repmat(2, [nperf, 1]); J = (1 : nperf).' + 1; K = repmat(5, [nperf, 1]); % Convert IJK-indices to linear index (as used in G) cellInx = sub2ind(G.cartDims, I, J, K); W = addWell(W, G, rock, cellInx, 'Name', 'producer'); % Plotting f = [ figure(1), figure(2) ]; set(0, 'CurrentFigure', f(1)); clf plotGrid(G, 'FaceColor', 'g', 'FaceAlpha', .3, 'EdgeColor', 'w'); plotWell(G, W); axis off, set(f(1), 'Color', 'w'), camproj perspective, view(3)


Initial conditions
We assume that the reservoir is initially at equilibrium. This means that the following condition must be satisfied: dp/dz = g rho, where g is the gavitational accelleration. This relation can be solved analytically for p, but alternatively one can solve the above ODE with 'initial condtition' p(z_0) = p_r:
gravity reset on g = norm(gravity); [z_0, z_max] = deal(0, max(G.cells.centroids(:,3)) + 5); press = ode23(@(z,p) g .* rho(p), [z_0, z_max], p_r); % We then interpolate onto the grid using cell centers: p_init = reshape(deval(press, G.cells.centroids(:,3)), [], 1); clear press
Setting up components needed for the simulation
Since we impose no-flow boundary conditions in this example, we restrict connections to interior faces only.
N = double(G.faces.neighbors); intInx = all(N ~= 0, 2); N = N(intInx, :); % We will be using the two-point flux approximation. First the one-sided % transmissibilities are computed, then the harmonic average is taken to % obtain the two-sided transmissibilites. hT = computeTrans(G, rock); cf = G.cells.faces(:,1); nf = G.faces.num; T = 1 ./ accumarray(cf, 1 ./ hT, [nf, 1]); T = T(intInx); % In setting up the equations, we need descrete forms of the divergence and % gradient operators, and we represent these as multiplication by sparse % matrices. In particular, we construct the 'gradient matrix' C as folows: n = size(N,1); C = sparse( [(1:n)'; (1:n)'], N, ones(n,1)*[1 -1], n, G.cells.num); % The discrete gradient and divergence operators are now given by grad = @(x)-C*x; div = @(x)C'*x; % Additionally, we will need to take the average of cell-based quantities % in neighboring cells and define the following function avg = @(x) 0.5 * (x(N(:,1)) + x(N(:,2)));
Pressure and well equations:
The pressure equation (without well contributions) is given by:
In discretized form, this leads to
z = G.cells.centroids(:,3); % z-coordinate of grid cells pressureEq = @(p, p0, dt) (1/dt) .* (pv(p).*rho(p) - pv(p0).*rho(p0)) ... - div( avg(rho(p) ./ mu) .* T .* grad(p - g*rho(p).*z) ); % Wellrates are given as Peaceman well-index times pressure drop wc = W(1).cells; % perforation grid cells WI = W(1).WI; % well indices dz = W(1).dZ; % perforation depth relative to well reference depth wellRates = ... @(p, bhp) WI .* (rho(p(wc)) ./ mu) .* (bhp - p(wc) + g*dz.*rho(p(wc)));
Define ADI variables
The primary variables are grid-cell pressures, well bhp and surface rate.
[p_ad, bhp_ad, qS_ad] = initVariablesADI(p_init, p_init(wc(1)), 0);
% For convenience, create indices to variables when stacked:
pIx = 1:G.cells.num; bhpIx = G.cells.num + 1; qSIx = G.cells.num + 2;
Set up simulation parameters
numSteps = 52; totTime = 365*day; dt = totTime / numSteps; tol = 1e-5; maxits = 10; % Save output in array 'sol' sol = repmat(struct('time', [], 'pressure', [], 'bhp', [], 'qS', []), ... [numSteps + 1, 1]); sol(1).time = 0; sol(1).pressure = double(p_ad); sol(1).bhp = double(bhp_ad); sol(1).qS = double(qS_ad); % Set up plot set(0, 'CurrentFigure', f(2)), clf, set(f(2), 'Color', 'w') subplot(2,1,1), plotCellData(G, convertTo(p_init, barsa)); title('pressure [bar]', 'EdgeColor', 'w'); colorbar, view(3), camproj perspective subplot(2,1,2); axis([0, convertTo(totTime,day), 0, 300]); title('Surface volume rate [m^3/day]'); hold on

Main simulation
t = 0; step = 0; nDigits = floor(log10(maxits)) + 1; while t < totTime, t = t + dt; step = step + 1; fprintf('\nTime step %d: Time %.2f -> %.2f days\n', ... step, convertTo(t - dt, day), convertTo(t, day)); % Newton loop resNorm = 1e99; p0 = double(p_ad); % Previous step pressure nit = 0; while (resNorm > tol) && (nit < maxits) % Create equations: eqs = cell([3, 1]); eqs{1} = pressureEq(p_ad, p0, dt); % Add well contributions in perforated cells: eqs{1}(wc) = eqs{1}(wc) - wellRates(p_ad, bhp_ad); % Sum of wellrates should equal total rate: eqs{2} = qS_ad - sum(wellRates(p_ad, bhp_ad))/rhoS; % Final equation is prescribed bhp eqs{3} = bhp_ad - 100*barsa; % Concatenate equations and solve: eq = cat(eqs{:}); J = eq.jac{1}; % Jacobian res = eq.val; % residual upd = -(J \ res); % Newton update % Update variables p_ad.val = p_ad.val + upd(pIx); bhp_ad.val = bhp_ad.val + upd(bhpIx); qS_ad.val = qS_ad.val + upd(qSIx); resNorm = norm(res); nit = nit + 1; fprintf(' Iteration %*d: Res = %.4e\n', nDigits, nit, resNorm); end if nit > maxits, error('Newton solves did not converge') else sol(step+1).time = t; sol(step+1).pressure = double(p_ad); sol(step+1).bhp = double(bhp_ad); sol(step+1).qS = double(qS_ad); % Plot evolution set(0, 'CurrentFigure', f(2)); subplot(2,1,1), cla, caxis([120, 205]) plotCellData(G, convertTo(sol(step+1).pressure, barsa), ... 'EdgeColor', 'w'); subplot(2,1,2) plot(convertTo(sol(step+1).time, day), ... convertTo(-sol(step+1).qS , meter^3/day), '*'); drawnow end end
Time step 1: Time 0.00 -> 7.02 days Iteration 1: Res = 1.0188e+07 Iteration 2: Res = 2.9972e-03 Iteration 3: Res = 2.5918e-07 Time step 2: Time 7.02 -> 14.04 days Iteration 1: Res = 1.1533e-01 Iteration 2: Res = 3.9810e-04 Iteration 3: Res = 5.1449e-09 Time step 3: Time 14.04 -> 21.06 days Iteration 1: Res = 8.8389e-02 Iteration 2: Res = 2.0533e-04 Iteration 3: Res = 1.2974e-09 Time step 4: Time 21.06 -> 28.08 days Iteration 1: Res = 7.5844e-02 Iteration 2: Res = 1.3776e-04 Iteration 3: Res = 5.4019e-10 Time step 5: Time 28.08 -> 35.10 days Iteration 1: Res = 6.8064e-02 Iteration 2: Res = 1.0237e-04 Iteration 3: Res = 2.7391e-10 Time step 6: Time 35.10 -> 42.12 days Iteration 1: Res = 6.2911e-02 Iteration 2: Res = 8.2003e-05 Iteration 3: Res = 1.6048e-10 Time step 7: Time 42.12 -> 49.13 days Iteration 1: Res = 5.9307e-02 Iteration 2: Res = 6.9692e-05 Iteration 3: Res = 1.0606e-10 Time step 8: Time 49.13 -> 56.15 days Iteration 1: Res = 5.6629e-02 Iteration 2: Res = 6.1909e-05 Iteration 3: Res = 7.7960e-11 Time step 9: Time 56.15 -> 63.17 days Iteration 1: Res = 5.4507e-02 Iteration 2: Res = 5.6675e-05 Iteration 3: Res = 6.2621e-11 Time step 10: Time 63.17 -> 70.19 days Iteration 1: Res = 5.2720e-02 Iteration 2: Res = 5.2860e-05 Iteration 3: Res = 5.3604e-11 Time step 11: Time 70.19 -> 77.21 days Iteration 1: Res = 5.1141e-02 Iteration 2: Res = 4.9831e-05 Iteration 3: Res = 4.7679e-11 Time step 12: Time 77.21 -> 84.23 days Iteration 1: Res = 4.9696e-02 Iteration 2: Res = 4.7242e-05 Iteration 3: Res = 4.3248e-11 Time step 13: Time 84.23 -> 91.25 days Iteration 1: Res = 4.8341e-02 Iteration 2: Res = 4.4914e-05 Iteration 3: Res = 3.9555e-11 Time step 14: Time 91.25 -> 98.27 days Iteration 1: Res = 4.7051e-02 Iteration 2: Res = 4.2754e-05 Iteration 3: Res = 3.6265e-11 Time step 15: Time 98.27 -> 105.29 days Iteration 1: Res = 4.5813e-02 Iteration 2: Res = 4.0714e-05 Iteration 3: Res = 3.3235e-11 Time step 16: Time 105.29 -> 112.31 days Iteration 1: Res = 4.4617e-02 Iteration 2: Res = 3.8772e-05 Iteration 3: Res = 3.0415e-11 Time step 17: Time 112.31 -> 119.33 days Iteration 1: Res = 4.3458e-02 Iteration 2: Res = 3.6916e-05 Iteration 3: Res = 2.7784e-11 Time step 18: Time 119.33 -> 126.35 days Iteration 1: Res = 4.2333e-02 Iteration 2: Res = 3.5142e-05 Iteration 3: Res = 2.5337e-11 Time step 19: Time 126.35 -> 133.37 days Iteration 1: Res = 4.1240e-02 Iteration 2: Res = 3.3445e-05 Iteration 3: Res = 2.3071e-11 Time step 20: Time 133.37 -> 140.38 days Iteration 1: Res = 4.0177e-02 Iteration 2: Res = 3.1823e-05 Iteration 3: Res = 2.0981e-11 Time step 21: Time 140.38 -> 147.40 days Iteration 1: Res = 3.9142e-02 Iteration 2: Res = 3.0275e-05 Iteration 3: Res = 1.9061e-11 Time step 22: Time 147.40 -> 154.42 days Iteration 1: Res = 3.8135e-02 Iteration 2: Res = 2.8798e-05 Iteration 3: Res = 1.7303e-11 Time step 23: Time 154.42 -> 161.44 days Iteration 1: Res = 3.7155e-02 Iteration 2: Res = 2.7390e-05 Iteration 3: Res = 1.5696e-11 Time step 24: Time 161.44 -> 168.46 days Iteration 1: Res = 3.6201e-02 Iteration 2: Res = 2.6048e-05 Iteration 3: Res = 1.4230e-11 Time step 25: Time 168.46 -> 175.48 days Iteration 1: Res = 3.5272e-02 Iteration 2: Res = 2.4770e-05 Iteration 3: Res = 1.2897e-11 Time step 26: Time 175.48 -> 182.50 days Iteration 1: Res = 3.4367e-02 Iteration 2: Res = 2.3554e-05 Iteration 3: Res = 1.1685e-11 Time step 27: Time 182.50 -> 189.52 days Iteration 1: Res = 3.3487e-02 Iteration 2: Res = 2.2396e-05 Iteration 3: Res = 1.0584e-11 Time step 28: Time 189.52 -> 196.54 days Iteration 1: Res = 3.2629e-02 Iteration 2: Res = 2.1295e-05 Iteration 3: Res = 9.5844e-12 Time step 29: Time 196.54 -> 203.56 days Iteration 1: Res = 3.1794e-02 Iteration 2: Res = 2.0247e-05 Iteration 3: Res = 8.6781e-12 Time step 30: Time 203.56 -> 210.58 days Iteration 1: Res = 3.0981e-02 Iteration 2: Res = 1.9250e-05 Iteration 3: Res = 7.8563e-12 Time step 31: Time 210.58 -> 217.60 days Iteration 1: Res = 3.0189e-02 Iteration 2: Res = 1.8303e-05 Iteration 3: Res = 7.1118e-12 Time step 32: Time 217.60 -> 224.62 days Iteration 1: Res = 2.9418e-02 Iteration 2: Res = 1.7401e-05 Iteration 3: Res = 6.4371e-12 Time step 33: Time 224.62 -> 231.63 days Iteration 1: Res = 2.8667e-02 Iteration 2: Res = 1.6544e-05 Iteration 3: Res = 5.8258e-12 Time step 34: Time 231.63 -> 238.65 days Iteration 1: Res = 2.7936e-02 Iteration 2: Res = 1.5729e-05 Iteration 3: Res = 5.2729e-12 Time step 35: Time 238.65 -> 245.67 days Iteration 1: Res = 2.7224e-02 Iteration 2: Res = 1.4955e-05 Iteration 3: Res = 4.7717e-12 Time step 36: Time 245.67 -> 252.69 days Iteration 1: Res = 2.6530e-02 Iteration 2: Res = 1.4218e-05 Iteration 3: Res = 4.3179e-12 Time step 37: Time 252.69 -> 259.71 days Iteration 1: Res = 2.5854e-02 Iteration 2: Res = 1.3517e-05 Iteration 3: Res = 3.9075e-12 Time step 38: Time 259.71 -> 266.73 days Iteration 1: Res = 2.5196e-02 Iteration 2: Res = 1.2851e-05 Iteration 3: Res = 3.5357e-12 Time step 39: Time 266.73 -> 273.75 days Iteration 1: Res = 2.4555e-02 Iteration 2: Res = 1.2218e-05 Iteration 3: Res = 3.1992e-12 Time step 40: Time 273.75 -> 280.77 days Iteration 1: Res = 2.3930e-02 Iteration 2: Res = 1.1616e-05 Iteration 3: Res = 2.8946e-12 Time step 41: Time 280.77 -> 287.79 days Iteration 1: Res = 2.3322e-02 Iteration 2: Res = 1.1044e-05 Iteration 3: Res = 2.6191e-12 Time step 42: Time 287.79 -> 294.81 days Iteration 1: Res = 2.2730e-02 Iteration 2: Res = 1.0500e-05 Iteration 3: Res = 2.3697e-12 Time step 43: Time 294.81 -> 301.83 days Iteration 1: Res = 2.2152e-02 Iteration 2: Res = 9.9826e-06 Time step 44: Time 301.83 -> 308.85 days Iteration 1: Res = 2.1590e-02 Iteration 2: Res = 9.4908e-06 Time step 45: Time 308.85 -> 315.87 days Iteration 1: Res = 2.1042e-02 Iteration 2: Res = 9.0233e-06 Time step 46: Time 315.87 -> 322.88 days Iteration 1: Res = 2.0509e-02 Iteration 2: Res = 8.5787e-06 Time step 47: Time 322.88 -> 329.90 days Iteration 1: Res = 1.9989e-02 Iteration 2: Res = 8.1561e-06 Time step 48: Time 329.90 -> 336.92 days Iteration 1: Res = 1.9482e-02 Iteration 2: Res = 7.7543e-06 Time step 49: Time 336.92 -> 343.94 days Iteration 1: Res = 1.8989e-02 Iteration 2: Res = 7.3723e-06 Time step 50: Time 343.94 -> 350.96 days Iteration 1: Res = 1.8508e-02 Iteration 2: Res = 7.0092e-06 Time step 51: Time 350.96 -> 357.98 days Iteration 1: Res = 1.8040e-02 Iteration 2: Res = 6.6639e-06 Time step 52: Time 357.98 -> 365.00 days Iteration 1: Res = 1.7583e-02 Iteration 2: Res = 6.3356e-06
