adjoint: Two-phase, incompressible adjoint solvers

Implements strategies for production optimisation based on adjoint formulations. This enables for, instance, optimization of net present value constrained by the bottom-hole pressure in wells. This module is limited to two-phase, incompressible flow as implemented in the incomp module. For optimization problems with more complex fluid physics, the newer “optimization” module is recommended.

Utilities

Contents

ADJOINT Add-on module for adjoint-based production optimisation

Files
addAdjointWellFields - SYNOPSIS: adjointFluidFields - Extend fluid functionality with fields needed in (2nd order) adjoint imp. assembleWellSystem - Generate pressure linear system components for wells. computeAdjointRHS - Compute adjoint ‘pressure’ rhs computeGradient - compute gradient for control variables and project according to computeNumericalGradient - compute numerical gradient controls2RHS - Create mappings A_N, b_N, A_D, b_D such that controls2Wells - Create mappings A_N, b_N, A_D, b_D such that generateUpstreamTransportMatrix - generateUpstreamTransportMatrix for use in saturation solver initControls - initControls – Initialize control structure based on well schedule initSchedule - initSchedule – Initialize schedule structure based on well W. optimizeObjective - optimizeObjective – Run whole optimization proccess using ad-hoc line projectGradient - Project gradient according to linear input constraints. Handles box-constraints and runAdjoint - runAdjoint – Run adjoint simulation based on simRes and schedule. runSchedule - runSchedule – Run simulation based on schedule. solveAdjointPressureSystem - Find current time step (search for empty slots in adjRes) solveAdjointTransportSystem - Find current time step (search for empty slots in adjRes) updateSchedule - Update schedule based on controls updateWells - Update wells based on schedule time step dispControls - Display control values. dispSchedule - Display schedule values lineSearchAgr - Run agressive line search based on given gradient. solveIncompFlowLocal - Local version of solveIncompFlow for use with adjoint module. Local
addAdjointWellFields(CG, W, varargin)

Synopsis:

W = addAdjointWellFields(CG, W, varargin)
W = addAdjointWellFields(CG, W, 'OverlapWell', 0, 'OverlapBlock', 4);

Description:

Hack for the adjoint code: Add additional fields to the Well-structure. The added fields are coarseCells and optionally CS.overlap and CS.wellOverlap.

Needed in updateWells and createSingleCellPseudoWells.

adjointFluidFields(fluid)

Extend fluid functionality with fields needed in (2nd order) adjoint imp.

Synopsis:

fluid = adjointFluidFields(fluid)
Parameters:fluid – A ‘fluid_structure’.
Returns:fluid – Updated fluid structure now containing additional fields - dLtInv (state) – d/ds (1 / lambda_t(state)) - d2LtInv(state) – d^2/ds^2 (1 / lambda_t(state)) - d2fw (state) – d^2/ds^2 f_w(state)

See also

fluid_structure.

assembleWellSystem(G, W, varargin)

Generate pressure linear system components for wells.

Synopsis:

W = assembleWellSystem(G, W)
W = assembleWellSystem(G, W, 'pn1', pv1, ...)
Parameters:
  • G – Grid structure as described by grid_structure.
  • W – Well structure as defined by addWell &c.
  • 'pn'/pv

    List of ‘key’/value pairs defining optional parameters. The supported options are:

    • Type – The kind of system to assemble. The choice made
      for this option influences which pressure solvers can be employed later. String. Default value = ‘hybrid’.
      Supported values are:
      • ’hybrid’ : Hybrid system with inverse mass matrix
        (BI) for Schur complement reduction.
      • ’mixed’ : Hybrid system with regular mass matrix (B)
        for reduction to mixed form.

      Note: The value of this option should match the option pair passed to function computeMimeticIP.

Returns:

W – Updated well structure. Function assembleWellSystem adds field ‘S’ to each well in ‘W’. The well system W(k).S has the following fields:

S.BI/S.B – Well hybrid system (inverse) ‘mass’ matrix. S.C – Well hybrid system discrete divergence operator. S.D – Well hybrid system flux continuity matrix. S.RHS – Well hybrid linear system right hand side.

computeAdjointRHS(G, W, f_res)

Compute adjoint ‘pressure’ rhs for use in e.g. function solveAdjointPressureSystem

Synopsis:

b = computeAdjoint(G, W, f_res, f_w)

DESCRIPTION: Computes adjoint ‘pressure’ rhs as input to solveIncompFlow in function solveAdjointPressureSystem

Parameters:
  • G – Grid data structure.
  • W – Well structure as defined by addWell &c.
  • f_res – Adjoint reservoir ‘pressure’ condtions
Returns:

b – Ajoint pressure rhs to be passed directly as option ‘rhs’ to solveIncompFlow.

computeGradient(W, adjRes, schedule, controls)

compute gradient for control variables and project according to linEqConst A*u = b => A*grad = 0 Thus the projected gradient is

grad_p = (I - A’*inv(A*A’)*A)*grad
computeNumericalGradient(simRes, G, S, W, rock, fluid, schedule, controls, objectiveFunction)

compute numerical gradient

controls2RHS(W, schedule, controls)
Create mappings A_N, b_N, A_D, b_D such that
q_{tot,N}^n = A_N{n}*u^n + b_N{n} p_{w,D}^n = A_D{n}*u^n + b_D{n}

where u^n is the set of controll variables at step n

controls2Wells(W, schedule, controls)
Create mappings A_N, b_N, A_D, b_D such that
q_{tot,N}^n = A_N{n}*u^n + b_N{n} p_{w,D}^n = A_D{n}*u^n + b_D{n}

where u^n is the set of controll variables at step n

dispControls(controls, schedule, varargin)

Display control values.

dispSchedule(schedule, varargin)

Display schedule values

generateUpstreamTransportMatrix(G, S, W, resSol, wellSol, varargin)

generateUpstreamTransportMatrix for use in saturation solver

SYNOPSIS: [A, qPluss, signQ] = generateUpstreamTransportMatrix(G, S, W, resSol, …

wellSol, pn1, pv1, …)

Description:

Generates sparse matrix A(cellFlux) which is used in transport solver s1 = s0 + dt*Dv*(Af(s) + q+). Assumes no-flow boundary conditions on all cell-faces

signQ is useful for differentation of max(q, 0)/min(q, 0) wrt to q, when q is zero

REQUIRED PARAMETERS:

Keyword Arguments:
 
  • Transpose – if true, the transpose of a is given (default false)
  • VectorOutput – if true, output A is a struct with fields ‘i’, ‘j’ and ‘qMinus’, such that A = sparse(i, j, -cellFlux) + diag(qMinus) (NOTE MINUSES). Default value is false
  • RelativeThreshold – Considers values below max(abs(cellflux))*RealtiveThreshold as zero (default 0)
initControls(schedule, varargin)

initControls – Initialize control structure based on well schedule

Synopsis:

controls = initControls(schedule, 'pn', pv, ...)

Description:

Initialize controls

Parameters:
  • schedule – schedule structure
  • 'pn'/pv

    List of ‘key’/value pairs defining optional parameters. The supported options are:

    • ControllableWells : indices to wells which will be
      controlled (default all)
    • BHPMaxMin : max/min value for bhp-wells,(default [-Inf Inf])
    • RateMaxMin : max/min value for rate-wells,(default [-Inf Inf])
    • MaxMin : matrix of size numControls x 2,
      where each row contains the max and min value for the corresponding control. Aternative to the two above
    • NumControlSteps : number of control steps (default number of time steps)
    • LinEqConst : linear equality constraints of the
      form Au = b given in the form {A_1, b_1, A_2, b_2, …}
    • Verbose : Display Control vars using dispControls
Returns:

controls – Initialized control structure having fields - well : #controllable wells x 1 structure having

fields:

  • wellNum : index to current well in schedule (and W)
  • values : values for each timeStep
  • bhpMaxMin :
  • rateMAxMin :
  • linEqConst : linear equality contraints structure having fields:
    • A
    • b

See also

initSchedule

initSchedule(W, varargin)

initSchedule – Initialize schedule structure based on well W.

Synopsis:

shedule = initSchedule(W, 'pn', pv, ...)

Description:

Initialize schedule

Parameters:
  • W – well structure
  • 'pn'/pv

    List of ‘key’/value pairs defining optional parameters. The supported options are:

    • NumSteps : number of simulation time steps (default 1)
    • TotalTime : total simualtion time (default 1)
    • TimeSteps : endtime for each time step assuming t_0 = 0 (alterative to two previous pns)
    • Verbose : display schedule with dispSchedule
Returns:

schedule – Initialized numSteps x 1 rate schedule structure having fields - timeInterval – [startTime endTime] - names – {name_1, …, name_n} - types – {welltype_1, … , welltype_n} - values – {val_1, …, val_2}

See also

dispSchedule

lineSearchAgr(simRes, G, S, W, rock, fluid, schedule, controls, grad, objectiveFunction, stepSize, figProps, opt)

Run agressive line search based on given gradient. Handles box-constraints and linear equality - and (probably not) inequality-constaints based on iteratively applying the constrints to the gradient until convergence.

Synopsis:

[simRes, schedule, controls, data] = lineSearch(...
  simRes, G, S, W, rock, fluid, schedule, controls, grad, objectiveFunction, ...
  stepSize, figProps, opt)
DISCRIPTION:
  1. Project gradient according to constraints iteratively until tollerance ConstTol is met or max number of iterations MaxConstIts is met (returnes failure). Constraints are applied in the following order:

    1. Box const.
    1. Lin. UnEq. const.
    1. Lin. Eq. const.

    The resulting norm of the projected gradient is used as stopping criteria

  2. Line search along projected gradient:

    Uses three points [x1 x2 x3] = [0 .5 1]*stepSize (a) If projected gradient pgrad(x2)=pgrad(x3), then on boundary,

    done

    1. If obj(x1)<obj(x2)<obj(x3), set stepSize=2*stepSize goto (a)
    2. If obj(x1)>obj(x2), set stepSize = .5*stepSize goto (a)
    3. Find max on quad-curve through (x1, x2, x3) done
Parameters:
  • S, W, rock, fluid (G,) – usual structures
  • simRes

  • controls (schedule,) –

  • grad – gradien as given by computeGradient
  • objectiveFunction – handle to objective function
  • 'pn'/pv
    List of ‘key’/value pairs defining optional parameters. The
    supported options are:
    • MaxPoints : maximum number of points before to
      terminate line search algorithm
    • LinSrchTol : line search relative tolerance
    • StepSize : Step size
    • ConstTol : Relative contraint satisfaction tol
    • MaxConstIts : max number of its for constraint satisfaction
    • VerboseLevel : amount of output to screen
Returns:
  • simRes – Results for ‘best’ forward simulation

  • schedule

  • controls

  • data – structure with fields value : objective function value for best run relNormGrad : relative norm of gradient |du|/|objective| success : whether or line search procedure succeeded fraction : optimal fraction obtained during the line

    search

SEE ALSO:

optimizeObjective(G, S, W, rock, fluid, resSolInit, schedule, controls, objectiveFunction, varargin)

optimizeObjective – Run whole optimization proccess using ad-hoc line search. A linkage with an external optimizer or use of matlabs optimizer toolbox (e.g., fmincon)is recommended.

Synopsis:

[simRes, schedule, controls, output] = optimizeObjective( ...
                     G, S, W, rock, fluid, resSolInit, ...
                     schedule, controls, objectiveFunction, varargin)
Parameters:
  • S, W, rock, fluid (G,) – usual structures
  • resSolInit – initial ‘solution’ minimum containing field resSol.sw
  • controls (schedule,) –

  • objectiveFunction – handle to objective function
  • 'pn'/pv – List of ‘key’/value pairs defining optional parameters. The supported options are:

:param : gradTol : stopping criterion for scaled gradien :param : objChangeTol : stopping criterion for realitive change in objective

function

:param : stepSize : initial step size :param : VerboseLevel : level of output to screen during progress (default: 0).

< 0 : no output
0 : minumal 1 : quite a bit 2 : loads
:param : PlotProgress : whether or not to plot output during progress
(default: true)
:param : OutputLevel : level of output given in structure output (default: 0)
< 0 = nothing (empty)
0 = objective function value for every
iteration

1 = ?

Returns:
  • simRes – Results for last (optimal) simulation
  • schedule
  • controls
  • outPut – as described above

SEE ALSO:

projectGradient(controls, du, varargin)

Project gradient according to linear input constraints. Handles box-constraints and linear equality - and inequality-constraints based on iteratively applying the constraints to the gradient until convergence.

Synopsis:

[pGrad] = projectGradient(...
  simRes, G, S, W, rock, fluid, schedule, controls, grad, objectiveFunction, varargin)
DISCRIPTION:

Project gradient according to constraints iteratively until tollerance ConstTol is met or max number of iterations MaxConstIts is met (returnes failure). Constraints are applied in the following order:

  1. Box const.
  1. Lin. InEq. const.
  1. Lin. Eq. const.
Parameters:
  • grad (controls,) –
  • 'pn'/pv
    List of ‘key’/value pairs defining optional parameters. The
    supported options are:
    • ConstTol : Relative contraint satisfaction tol
    • MaxConstIts : max number of its for constraint satisfaction
    • VerboseLevel : amount of output to screen
Returns:

pGrad – projected gradient

SEE ALSO:

runAdjoint(simRes, G, S, W, rock, fluid, schedule, controls, objectiveFunction, varargin)

runAdjoint – Run adjoint simulation based on simRes and schedule.

Synopsis:

adjRes = runAdjoint(simRes, G, S, W, fluid, schedule, objective, pn, pv, ...)

DESCRIPTION:

Parameters:
  • simRes
  • G – Grid data structure.
  • S
  • W
  • fluid
  • schedule
  • objective – function handle
Returns:

adjRes – numSteps x 1 structure having fields - timeInterval - resSol - wellSol

SEE ALSO:

runSchedule(resSolInit, G, S, W, rock, fluid, schedule, varargin)

runSchedule – Run simulation based on schedule.

Synopsis:

simRes = runSchedule(resSolInit, G, S, W, fluid, schedule, pn, pv, ...)

DESCRIPTION:

Parameters:
  • resSolInit
  • G – Grid data structure.
  • S
  • W
  • fluid
  • schedule
Returns:

simRes – (numSteps+1) x 1 structure having fields - timeInterval - resSol - wellSol

SEE ALSO:

solveAdjointPressureSystem(G, S, W, rock, fluid, simRes, adjRes, obj, varargin)

Find current time step (search for empty slots in adjRes) NOTE: actually curent time step +1

solveAdjointTransportSystem(G, S, W, rock, fluid, simRes, adjRes, obj, varargin)

Find current time step (search for empty slots in adjRes) NOTE: actually curent time step +1

solveIncompFlowLocal(state, g, s, fluid, varargin)

Local version of solveIncompFlow for use with adjoint module. Local version has the additional option to supply right-hand-side to system directly. In addition, there is som extra slack in the assertion statement that checks sum(rates)=0 when only rate-controlled wells are present. This to prevent crash if input-constraint handling is not set to machine precision.

Synopsis:

state = solveIncompFlow(state, G, S, fluid)
state = solveIncompFlow(state, G, S, fluid, 'pn1', pv1, ...)

Description:

This function assembles and solves a (block) system of linear equations defining interface fluxes and cell and interface pressures at the next time step in a sequential splitting scheme for the reservoir simulation problem defined by Darcy’s law and a given set of external influences (wells, sources, and boundary conditions).

Parameters:
  • state – Reservoir and well solution structure either properly initialized from function ‘initState’, or the results from a previous call to function ‘solveIncompFlow’ and, possibly, a transport solver such as function ‘explicitTransport’.
  • S (G,) – Grid and (mimetic) linear system data structures as defined by function ‘computeMimeticIP’.
  • fluid – Fluid data structure as described by ‘fluid_structure’.
Keyword Arguments:
 
  • wells – Well structure as defined by function ‘addWell’. May be empty (i.e., W = []) which is interpreted as a model without any wells.

  • bc – Boundary condition structure as defined by function ‘addBC’. This structure accounts for all external boundary conditions to the reservoir flow. May be empty (i.e., bc = []) which is interpreted as all external no-flow (homogeneous Neumann) conditions.

  • src – Explicit source contributions as defined by function ‘addSource’. May be empty (i.e., src = []) which is interpreted as a reservoir model without explicit sources.

  • rhs – Supply system right-hand side ‘b’ directly. Overrides internally constructed system right-hand side. Must be a three-element cell array, the elements of which are correctly sized for the block system component to be replaced.

    NOTE: This is a special purpose option for use by code which needs to modify the system of linear equations directly, e.g., the ‘adjoint’ code.

  • Solver – Which solver mode function ‘solveIncompFlow’ should employ in assembling and solving the block system of linear equations. String. Default value: Solver = ‘hybrid’.

    Supported values are:
    • ‘hybrid’ –

      Assemble and solve hybrid system for interface pressures. System is eventually solved by Schur complement reduction and back substitution.

      The system ‘S’ must in this case be assembled by passing option pair (‘Type’,’hybrid’) or option pair (‘Type’,’comp_hybrid’) to function ‘computeMimeticIP’.

    • ‘mixed’ –

      Assemble and solve a hybrid system for interface pressures, cell pressures and interface fluxes. System is eventually reduced to a mixed system as per function ‘mixedSymm’.

      The system ‘S’ must in this case be assembled by passing option pair (‘Type’,’mixed’) or option pair (‘Type’,’comp_hybrid’) to function ‘computeMimeticIP’.

    • ‘tpfa’ –

      Assemble and solve a cell-centred system for cell pressures. Interface fluxes recovered through back substitution.

      The system ‘S’ must in this case be assembled by passing option pair (‘Type’,’mixed’) or option pair (‘Type’,’comp_hybrid’) to function ‘computeMimeticIP’.

  • LinSolve – Handle to linear system solver software to which the fully assembled system of linear equations will be passed. Assumed to support the syntax

    x = LinSolve(A, b)

    in order to solve a system Ax=b of linear equations. Default value: LinSolve = @mldivide (backslash).

  • MatrixOutput – Whether or not to return the final system matrix ‘A’ to the caller of function ‘solveIncompFlow’. Logical. Default value: MatrixOutput = FALSE.

Returns:

state – Update reservoir and well solution structure with new values for the fields:

  • pressure – Pressure values for all cells in the

    discretised reservoir model, ‘G’.

  • facePressure –

    Pressure values for all interfaces in the discretised reservoir model, ‘G’.

  • flux – Flux across global interfaces corresponding to

    the rows of ‘G.faces.neighbors’.

  • A – System matrix. Only returned if specifically

    requested by setting option ‘MatrixOutput’.

  • wellSol – Well solution structure array, one element for

    each well in the model, with new values for the fields:

    • flux – Perforation fluxes through all
      perforations for corresponding well. The fluxes are interpreted as injection fluxes, meaning positive values correspond to injection into reservoir while negative values mean production/extraction out of reservoir.
    • pressure – Well bottom-hole pressure.

Note

If there are no external influences, i.e., if all of the structures ‘W’, ‘bc’, and ‘src’ are empty and there are no effects of gravity, and no system right hand side has been supplied externally, then the input values ‘xr’ and ‘xw’ are returned unchanged and a warning is printed in the command window. This warning is printed with message ID

‘solveIncompFlow:DrivingForce:Missing’
updateSchedule(controls, schedule)

Update schedule based on controls

updateWells(W, scheduleStep)

Update wells based on schedule time step

Contents

Files recovery - Objective function calculating water volume at last time step simpleNPV - Simple net-present-value function - no discount factor

recovery(G, S, W, rock, fluid, simRes, schedule, controls, varargin)

Objective function calculating water volume at last time step

Synopsis:

obj = (G, S, W, rock, fluid, simRes, schedule, controls, varargin)

Description:

Computes value of objective function for given simulation, and partial derivatives of variables if varargin > 6

Parameters:simRes
Returns:obj – structure with fields val - value of objective function

SEE ALSO:

simpleNPV(G, S, W, rock, fluid, simRes, schedule, controls, varargin)

Simple net-present-value function - no discount factor

Synopsis:

obj = simpleNPV(G, S, W, rock, fluid, simRes, schedule, controls)

Description:

Computes value of objective function for given simulation, and partial derivatives of variables if varargin > 6

Parameters:simRes
Returns:obj – structure with fields

SEE ALSO:

Examples

Compare Gradient Computed Numerically and by Adjoint Equations

Generated from compareGradients.m

mrstModule add adjoint mimetic incomp

Setup model

verbose = true;
verboseLevel = 1;

nx = 10; ny = 10; nz = 1;
G  = cartGrid([nx ny nz]);
G  = computeGeometry(G);

rock.perm = exp(( 3*rand(nx*ny, 1) + 1))*100*milli*darcy;
rock.perm = ones( size(rock.perm) )*milli*darcy;
rock.poro = repmat(0.3, [G.cells.num, 1]);

fluid  = initCoreyFluid('mu' , [   1,   5].*centi*poise    , ...
                        'rho', [1014, 859]*kilogram/meter^3, ...
                        'n'  , [   2,   2]                 , ...
                        'sr' , [   0,   0]                 , ...
                        'kwm', [   1,   1]);

fluid  = adjointFluidFields(fluid);

S = computeMimeticIP(G, rock, 'Type', 'comp_hybrid', 'Verbose', verbose);
Processing Cells   1 : 100 of 100 ... done (0.00 [s], 5.02e+04 cells/second)
Total 3D Geometry Processing Time = 0.002 [s]
Using inner product: 'ip_simple'.
Computing cell inner products ...             Elapsed time is 0.028347 seconds.
Assembling global inner product matrix ...    Elapsed time is 0.000327 seconds.
Max error in inverse = 2.9976e-15

Choose objective function

objectiveFunction = str2func('simpleNPV');
%objectiveFunction = str2func('recovery');

Introduce wells

radius = .1;
W = addWell([], G, rock, 1         , 'Type', 'bhp' , 'Val',  100*barsa, 'Radius', radius, 'Name', 'i1', 'Comp_i', [1, 0], 'InnerProduct', 'ip_tpf');
W = addWell( W, G, rock, nx        , 'Type', 'rate', 'Val',  -.5/day  , 'Radius', radius, 'Name', 'p1', 'Comp_i', [0, 1], 'InnerProduct', 'ip_tpf');
W = addWell( W, G, rock, nx*ny-nx+1, 'Type', 'rate', 'Val',  -.5/day  , 'Radius', radius, 'Name', 'p3', 'Comp_i', [0, 1], 'InnerProduct', 'ip_tpf');
W = addWell( W, G, rock, nx*ny     , 'Type', 'rate', 'Val',  -.5/day  , 'Radius', radius, 'Name', 'p3', 'Comp_i', [0, 1], 'InnerProduct', 'ip_tpf');

W = assembleWellSystem(G, W, 'Type', 'comp_hybrid');

resSolInit = initResSol(G, 0.0);
resSolInit.wellSol = initWellSol(W, 0);


totVol   = sum(G.cells.volumes.*rock.poro);
schedule = initSchedule(W, 'NumSteps', 10, 'TotalTime', totVol*day, 'Verbose', verbose);


controls = initControls(schedule, 'ControllableWells', (2:4), ...
                                  'Verbose', verbose, ...
                                  'NumControlSteps', 10);
Defaulting reference depth to top of formation for well i1. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well p1. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well p3. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well p3. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.

----------------- DISPLAYING SCHEDULE ----------------

Time interval :  0.00 - 259200.00
...

Forward run

simRes = runSchedule(resSolInit, G, S, W, rock, fluid, schedule, ...
                             'VerboseLevel', verboseLevel);
----------------- DISPLAYING SCHEDULE ----------------

Time interval :  0.00 - 259200.00
Well name           Type          Value
       i1            bhp       10000000
       p1           rate     -5.787e-06
       p3           rate     -5.787e-06
...

Adjoint run

adjRes = runAdjoint(simRes, G, S, W, rock, fluid, schedule, controls, ...
                    objectiveFunction, 'VerboseLevel', verboseLevel);
grad   = computeGradient(W, adjRes, schedule, controls);

numGrad = computeNumericalGradient(simRes, G, S, W, rock, fluid, ...
                                   schedule, controls, objectiveFunction)
adjGrad = cell2mat(grad)
******* Starting adjoint simulation *******
Time step  10 of  10,   Transport:    0.017 sec,   Pressure:    0.024 sec
Time step   9 of  10,   Transport:    0.006 sec,   Pressure:    0.012 sec
Time step   8 of  10,   Transport:    0.011 sec,   Pressure:    0.007 sec
Time step   7 of  10,   Transport:    0.002 sec,   Pressure:    0.006 sec
Time step   6 of  10,   Transport:    0.010 sec,   Pressure:    0.023 sec
Time step   5 of  10,   Transport:    0.007 sec,   Pressure:    0.006 sec
...

Plot results

igure; hold on
for k = 1 : size(numGrad, 1);
    plot(numGrad(k,:), '-ob');
    plot(adjGrad(k,:), '-xr');
end
legend('Numerical', 'Adjoint')
_images/compareGradients_01.png

Simple Adjoint Test Using BHP-control

Generated from simpleBHPOpt.m

mrstModule add adjoint mimetic incomp

% whether or not to show output
verbose = false;
verboseLevel = 0;

Define model

nx = 21; ny = 21; nz = 1;
G = cartGrid([nx ny nz], [5*nx 5*ny 1*nz]);
G = computeGeometry(G);

c = G.cells.centroids;
rock.perm  = max(10*sin(c(:,2)/25+.5*cos(c(:,1)/25))-9, .01)*1000*milli*darcy;
rock.poro  = repmat(0.3, [G.cells.num, 1]);

fluid  = initCoreyFluid('mu' , [1, 5] .* centi*poise, ...
                        'rho', [1014, 859].*kilogram/meter^3, ...
                        'n'  , [2, 2], 'sr', [0, 0], 'kwm', [1, 1]);
fluid  = adjointFluidFields(fluid);
Processing Cells   1 : 441 of 441 ... done (0.01 [s], 7.67e+04 cells/second)
Total 3D Geometry Processing Time = 0.006 [s]

Wells and initial rates

radius = .1;
totVol = sum(poreVolume(G, rock));
totTime = 500*day;
W = [];
% Injectors along left side:
nInj = 3; % > 1
pos  = (1 : (ny-1)/(nInj-1) : ny)';
posInj  = round(pos);
for k = 1:nInj
    nm = ['inj', num2str(k)];
    W = addWell(W, G, rock, 1+(posInj(k)-1)*nx, 'Type', 'bhp' , 'Val', 500*barsa, ...
                'Radius', radius, 'Name', nm, 'Comp_i', [1, 0], 'Sign', 1, 'InnerProduct', 'ip_tpf');
end
% Producers along right side:
nProd = 5; % >1
pos  = (1 : (ny-1)/(nProd-1) : ny)';
posProd  = round(pos);
for k = 1:nProd
    nm = ['prod', num2str(k)];
    W = addWell(W, G, rock, nx+(posProd(k)-1)*nx, 'Type', 'bhp' , 'Val', 150*barsa, ...
                'Radius', radius, 'Name', nm, 'Comp_i', [1, 0], 'Sign', -1, 'InnerProduct', 'ip_tpf');
end
Defaulting reference depth to top of formation for well inj1. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well inj2. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well inj3. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well prod1. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well prod2. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well prod3. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well prod4. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well prod5. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.

System components

S = computeMimeticIP(G, rock, 'Type', 'comp_hybrid', 'Verbose', verbose, ...
                     'InnerProduct', 'ip_tpf');
W = assembleWellSystem(G, W, 'Type', 'comp_hybrid');

Initialize

state = initResSol(G, 0.0);
state.wellSol = initWellSol(W, 0);

Objective function

objectiveFunction = str2func('simpleNPV');

Initialize schedule and controls

numSteps = 10;
schedule = initSchedule(W, 'NumSteps', numSteps, 'TotalTime', ...
                        totTime, 'Verbose', verbose);

% box constraints for each well [min rate, max rate]
box = [repmat([300*barsa 700*barsa], nInj, 1); repmat([100*barsa 200*barsa], nProd, 1)];
controls = initControls(schedule, 'ControllableWells', (1:numel(W)), ...
                                  'MinMax', box, ...
                                  'Verbose', verbose, ...
                                  'NumControlSteps', numSteps);

Run optimization

simRes, schedule, controls, out] = optimizeObjective(G, S, W, rock, ...
                                        fluid, state, schedule, ...
                                        controls, objectiveFunction, ...
                                        'gradTol',       1e-3, ...
                                        'objChangeTol',  5e-4, ...
                                        'VerboseLevel', verboseLevel);
********** STARTING ITERATION   1 ****************
Current stepsize: -1.00000
Forward solve   1:
----------------- DISPLAYING SCHEDULE ----------------

Time interval :  0.00 - 4320000.00
Well name           Type          Value
...
_images/simpleBHPOpt_01.png

Simple Adjoint Test Using Rate-Control

Generated from simpleRateOpt.m

mrstModule add adjoint mimetic incomp

% whether or not to show output
verbose = false;
verboseLevel = 2;

Define model

nx = 21; ny = 21; nz = 1;
G = cartGrid([nx ny nz], [5*nx 5*ny 1*nz]);
G = computeGeometry(G);

c = G.cells.centroids;
rock.perm  = max(10*sin(c(:,2)/25+.5*cos(c(:,1)/25))-9, .01)*1000*milli*darcy;
rock.poro  = repmat(0.3, [G.cells.num, 1]);

fluid  = initCoreyFluid('mu' , [1, 5] .* centi*poise, ...
                        'rho', [1014, 859].*kilogram/meter^3, ...
                        'n'  , [2, 2], 'sr', [0, 0], 'kwm', [1, 1]);
fluid  = adjointFluidFields(fluid);
Processing Cells   1 : 441 of 441 ... done (0.01 [s], 7.90e+04 cells/second)
Total 3D Geometry Processing Time = 0.006 [s]

Wells and initial rates

radius = .1;
totVol = sum(poreVolume(G, rock));
totTime = 500*day;
W = [];
% Injectors along left side:
nInj = 3; % > 1
pos  = (1 : (ny-1)/(nInj-1) : ny)';
posInj  = round(pos);
for k = 1:nInj
    nm = ['inj', num2str(k)];
    W = addWell(W, G, rock, 1+(posInj(k)-1)*nx, 'Type', 'rate' , 'Val', (1/nInj)*totVol/totTime, ...
                'Radius', radius, 'Name', nm, 'Comp_i', [1, 0], 'Sign', 1, 'InnerProduct', 'ip_tpf');
end
% Producers along right side:
nProd = 5; % >1
pos  = (1 : (ny-1)/(nProd-1) : ny)';
posProd  = round(pos);
for k = 1:nProd
    nm = ['prod', num2str(k)];
    W = addWell(W, G, rock, nx+(posProd(k)-1)*nx, 'Type', 'rate' , 'Val', -(1/nProd)*totVol/totTime, ...
                'Radius', radius, 'Name', nm, 'Comp_i', [1, 0], 'Sign', -1, 'InnerProduct', 'ip_simple');
end
Defaulting reference depth to top of formation for well inj1. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well inj2. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well inj3. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well prod1. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well prod2. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well prod3. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well prod4. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.
Defaulting reference depth to top of formation for well prod5. Please specify 'refDepth' optional argument to 'addWell' if you require bhp at a specific depth.

System components

S = computeMimeticIP(G, rock, 'Type', 'comp_hybrid', 'Verbose', verbose, ...
                     'InnerProduct', 'ip_tpf');
W = assembleWellSystem(G, W, 'Type', 'comp_hybrid');

Initialize

state = initResSol(G, 0.0);
state.wellSol = initWellSol(W, 0);

Objective function

objectiveFunction = str2func('simpleNPV');

Initialize schedule and controls

numSteps = 10;
schedule = initSchedule(W, 'NumSteps', numSteps, 'TotalTime', ...
                        totTime, 'Verbose', verbose);

% box constraints for each well [min rate, max rate]
box = [repmat([0 inf], nInj, 1); repmat([-inf 0], nProd, 1)];
controls = initControls(schedule, 'ControllableWells', (1:numel(W)), ...
                                  'MinMax', box, ...
                                  'Verbose', verbose, ...
                                  'NumControlSteps', numSteps, ...
                                  'LinEqConst', {ones(1, numel(W)), 0}');

Run optimization

simRes, schedule, controls, out] = optimizeObjective(G, S, W, rock, ...
                                        fluid, state, schedule, ...
                                        controls, objectiveFunction, ...
                                        'gradTol',       1e-3, ...
                                        'objChangeTol',  5e-4, ...
                                        'VerboseLevel', verboseLevel);
********** STARTING ITERATION   1 ****************
Current stepsize: -1.00000
----------------- DISPLAYING SCHEDULE ----------------

Time interval :  0.00 - 4320000.00
Well name           Type          Value
     inj1           rate     2.5521e-05
...
_images/simpleRateOpt_01.png