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:

$\frac{d}{dt}(\phi\rho)+\nabla\cdot(\rho v)=0, \quad v = -\frac{K}{\mu}\nabla(p-g\rho z)$

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