Reservoir simulation using Proper Orthogonal Decomposition (POD)
This example demonstrates the use of model reduction when solving a given system for many small variations of the same schedule. The example is meant to illustrate the principles, not best practice for real cases.
Contents
Setup
Load required modules and set random stream
require deckformat ad-fi s = RandStream('mcg16807','Seed',0); RandStream.setGlobalStream(s); % We set up a training simulation and an actual problem. The training % simulation has the same well and grid setupm, but the BHP of the % producers varies in the different simulations. current_dir = fileparts(mfilename('fullpath')); fn = fullfile(current_dir, 'training.data'); deck_training = readEclipseDeck(fn); deck_training = convertDeckUnits(deck_training); fn = fullfile(current_dir, 'problem.data'); deck_problem = readEclipseDeck(fn); deck_problem = convertDeckUnits(deck_problem); % The grid is common for both simulations grd = deck_training.GRID; G = initEclipseGrid(deck_training); G = computeGeometry(G); % rock - heterogenous field poro = gaussianField(G.cartDims, [.4 .8], [11 3 3], 2.5); K = poro.^3.*(1e-5)^2./(0.81*72*(1-poro).^2); rock.perm = K(:); rock.poro = poro(:); % fluid fluid = initDeckADIFluid(deck_training); gravity off state.pressure = 1.5e7*ones(G.cells.num, 1); state.s = ones(G.cells.num,1)*[0, 1]; schedule_training = deck_training.SCHEDULE; schedule_problem = deck_problem.SCHEDULE;
Solve full system for various parameters to create snapshot ensamble
systemOW = initADISystem({'Oil', 'Water'}, G, rock, fluid); [wsol, states] = runScheduleADI(state, G, rock, systemOW, schedule_training);
Step 1 of 47 (Used 5 iterations) Step 2 of 47 (Used 4 iterations) Step 3 of 47 (Used 5 iterations) Step 4 of 47 (Used 4 iterations) Step 5 of 47 (Used 4 iterations) Step 6 of 47 (Used 4 iterations) Step 7 of 47 (Used 9 iterations) Step 8 of 47 (Used 10 iterations) Step 9 of 47 (Used 6 iterations) Step 10 of 47 (Used 5 iterations) Step 11 of 47 (Used 5 iterations) Step 12 of 47 (Used 5 iterations) Step 13 of 47 (Used 5 iterations) Step 14 of 47 (Used 5 iterations) Step 15 of 47 (Used 5 iterations) Step 16 of 47 (Used 5 iterations) Step 17 of 47 (Used 5 iterations) Step 18 of 47 (Used 8 iterations) Step 19 of 47 (Used 10 iterations) Step 20 of 47 (Used 7 iterations) Step 21 of 47 (Used 6 iterations) Step 22 of 47 (Used 5 iterations) Step 23 of 47 (Used 7 iterations) Step 24 of 47 (Used 6 iterations) Step 25 of 47 (Used 5 iterations) Step 26 of 47 (Used 4 iterations) Step 27 of 47 (Used 4 iterations) Step 28 of 47 (Used 8 iterations) Step 29 of 47 (Used 5 iterations) Step 30 of 47 (Used 5 iterations) Step 31 of 47 (Used 5 iterations) Step 32 of 47 (Used 5 iterations) Step 33 of 47 (Used 5 iterations) Step 34 of 47 (Used 4 iterations) Step 35 of 47 (Used 5 iterations) Step 36 of 47 (Used 5 iterations) Step 37 of 47 (Used 4 iterations) Step 38 of 47 (Used 7 iterations) Step 39 of 47 (Used 6 iterations) Step 40 of 47 (Used 6 iterations) Step 41 of 47 (Used 7 iterations) Step 42 of 47 (Used 7 iterations) Step 43 of 47 (Used 7 iterations) Step 44 of 47 (Used 6 iterations) Step 45 of 47 (Used 5 iterations) Step 46 of 47 (Used 4 iterations) Step 47 of 47 (Used 4 iterations)
Plot the simulation for all timesteps
The dominant producer switches between each control. This is obviously not a good schedule for oil production, but it gives a highly dynamic flow pattern.
figure(1); for i = 1:numel(states) clf; subplot(2,1,1) plotCellData(G, states{i}.pressure) title('Pressure') subplot(2,1,2) plotCellData(G, states{i}.s(:,1)) title('Water saturation') pause(.1); end

Create basis for proper orthogonal decomposition
When doing model reduction, parts of the model can be reduced based on the assumption that the eigenspace of the simulation is similar to the eigenspace of the history we are using to construct the basis. As the pressure solution is costly and in general very smooth, we opt to reduce the pressure variable. This is done by a singular value decomposition and then using the largest eigenvalues to find the eigenvalues which capture the most essential patterns of pressure.
We reduce the number of pressure variables from 900 to 25. Saturation is more difficult to reduce accurately, requiring a magnitude more variables and clustering of eigenvectors. This example defaults to no clustering as the implementation of clustering depends on an external package YAEL (https://gforge.inria.fr/)
ns = nan; np = 25; basis_no_cluster = basisOW(states,... 'energyfraction_pressure', 1,... 'energyfraction_saturation',1,... 'maxno_pressure', np,... 'maxno_saturation', ns... ); basis = basis_no_cluster; % Copy system systemOW_basis = systemOW; % Add basis, set max iterations as the solution will not generally converge % when approximated by another eigenspace. systemOW_basis.podbasis = basis; systemOW_basis.nonlinear.maxIterations = 6;
25 of 47 eigenvalues included Including saturation equations in full!
Run another schedule both using pod and a full solution
The schedule is the same in terms of well setup, grid, porosity/permeability as well as the general flow pattern, but the rates have been changed. This is typical of an optimization loop: Several similar configurations which share many properties will be solved, either manually by a reservoir engineer or by a automated optimization program.
In general, a model reduction will not converge fully. We simply limit the number of iterations. A more sophisticated approach would involve setting the convergence limits based on the amount of reduction done.
timer = tic; [wsol_full, states_full] = runScheduleADI(state, G, rock, systemOW, schedule_problem); tfull = toc(timer); warning('off', 'newt:maxit') timer = tic; [wsol_pod, states_pod] = runScheduleADI(state, G, rock, systemOW_basis, schedule_problem); tpod = toc(timer); warning('on', 'newt:maxit')
Step 1 of 52 (Used 5 iterations) Step 2 of 52 (Used 4 iterations) Step 3 of 52 (Used 5 iterations) Step 4 of 52 (Used 4 iterations) Step 5 of 52 (Used 4 iterations) Step 6 of 52 (Used 4 iterations) Step 7 of 52 (Used 9 iterations) Step 8 of 52 (Used 14 iterations) Step 9 of 52 (Used 9 iterations) Step 10 of 52 (Used 8 iterations) Step 11 of 52 (Used 8 iterations) Step 12 of 52 (Used 8 iterations) Step 13 of 52 (Used 9 iterations) Step 14 of 52 (Used 11 iterations) Step 15 of 52 (Used 7 iterations) Step 16 of 52 (Used 5 iterations) Step 17 of 52 (Used 6 iterations) Step 18 of 52 (Used 10 iterations) Step 19 of 52 (Used 7 iterations) Step 20 of 52 (Used 6 iterations) Step 21 of 52 (Used 6 iterations) Step 22 of 52 (Used 6 iterations) Step 23 of 52 (Used 6 iterations) Step 24 of 52 (Used 5 iterations) Step 25 of 52 (Used 7 iterations) Step 26 of 52 (Used 6 iterations) Step 27 of 52 (Used 6 iterations) Step 28 of 52 (Used 8 iterations) Step 29 of 52 (Used 5 iterations) Step 30 of 52 (Used 6 iterations) Step 31 of 52 (Used 5 iterations) Step 32 of 52 (Used 6 iterations) Step 33 of 52 (Used 6 iterations) Step 34 of 52 (Used 7 iterations) Step 35 of 52 (Used 7 iterations) Step 36 of 52 (Used 7 iterations) Step 37 of 52 (Used 7 iterations) Step 38 of 52 (Used 10 iterations) Step 39 of 52 (Used 8 iterations) Step 40 of 52 (Used 5 iterations) Step 41 of 52 (Used 5 iterations) Step 42 of 52 (Used 4 iterations) Step 43 of 52 (Used 3 iterations) Step 44 of 52 (Used 3 iterations) Step 45 of 52 (Used 4 iterations) Step 46 of 52 (Used 3 iterations) Step 47 of 52 (Used 3 iterations) Step 48 of 52 (Used 3 iterations) Step 49 of 52 (Used 4 iterations) Step 50 of 52 (Used 3 iterations) Step 51 of 52 (Used 3 iterations) Step 52 of 52 (Used 3 iterations) Step 1 of 52 (Used 6 iterations) Step 2 of 52 (Used 6 iterations) Step 3 of 52 (Used 6 iterations) Step 4 of 52 (Used 6 iterations) Step 5 of 52 (Used 6 iterations) Step 6 of 52 (Used 6 iterations) Step 7 of 52 (Used 6 iterations) Step 8 of 52 (Used 6 iterations) Step 9 of 52 (Used 6 iterations) Step 10 of 52 (Used 6 iterations) Step 11 of 52 (Used 6 iterations) Step 12 of 52 (Used 6 iterations) Step 13 of 52 (Used 6 iterations) Step 14 of 52 (Used 6 iterations) Step 15 of 52 (Used 6 iterations) Step 16 of 52 (Used 6 iterations) Step 17 of 52 (Used 6 iterations) Step 18 of 52 (Used 6 iterations) Step 19 of 52 (Used 6 iterations) Step 20 of 52 (Used 6 iterations) Step 21 of 52 (Used 6 iterations) Step 22 of 52 (Used 6 iterations) Step 23 of 52 (Used 6 iterations) Step 24 of 52 (Used 6 iterations) Step 25 of 52 (Used 6 iterations) Step 26 of 52 (Used 6 iterations) Step 27 of 52 (Used 6 iterations) Step 28 of 52 (Used 6 iterations) Step 29 of 52 (Used 6 iterations) Step 30 of 52 (Used 6 iterations) Step 31 of 52 (Used 6 iterations) Step 32 of 52 (Used 6 iterations) Step 33 of 52 (Used 6 iterations) Step 34 of 52 (Used 6 iterations) Step 35 of 52 (Used 6 iterations) Step 36 of 52 (Used 6 iterations) Step 37 of 52 (Used 6 iterations) Step 38 of 52 (Used 6 iterations) Step 39 of 52 (Used 6 iterations) Step 40 of 52 (Used 6 iterations) Step 41 of 52 (Used 6 iterations) Step 42 of 52 (Used 6 iterations) Step 43 of 52 (Used 6 iterations) Step 44 of 52 (Used 6 iterations) Step 45 of 52 (Used 6 iterations) Step 46 of 52 (Used 6 iterations) Step 47 of 52 (Used 6 iterations) Step 48 of 52 (Used 6 iterations) Step 49 of 52 (Used 6 iterations) Step 50 of 52 (Used 6 iterations) Step 51 of 52 (Used 6 iterations) Step 52 of 52 (Used 6 iterations)
Plot and compare the solutions
v = [0, 90]; figure(1); for i = 1:numel(states_pod) clf; subplot(2,2,1) plotCellData(G, states_full{i}.pressure) title('Pressure, full solve'); axis tight; cp = caxis(); subplot(2,2,2) plotCellData(G, states_full{i}.s(:,1)) title('Water saturation, full solve'); axis tight; colorbar(); cs = caxis(); view(v); subplot(2,2,3) plotCellData(G, states_pod{i}.pressure) title('Pressure, reduced solve'); axis tight; caxis(cp); subplot(2,2,4) plotCellData(G, states_pod{i}.s(:,1)) title('Water saturation, reduced solve'); caxis(cs); axis tight; err = @(x, y) abs(x-y)/x; colorbar(); view(v); pause(.1); end

Plot the well rates
The oil rate is in general close to the reference. The water cuts show a similar error. Note that this example was made to produce interesting flow patterns, not realistic production (oil is being injected during certain time steps).
In terms of time used, these examples are so small that the overhead of setting up the linear systems is large compared to the cost of solving the actual systems. As such, the model
time = cumsum(schedule_problem.step.val) ; [qWsp, qOsp, qGsp, bhpp] = wellSolToVector(wsol_pod); [qWsf, qOsf, qGsf, bhpf] = wellSolToVector(wsol_full); for i = 2:5 figure(i); clf; subplot(1,2,1) plot(time, -[qWsf(:,i) , qWsp(:,i)]) title(['Water rate ' wsol_full{1}(i).name]) subplot(1,2,2) plot(time, -[qOsf(:,i) , qOsp(:,i)]) title(['Oil rate ' wsol_full{1}(i).name]) legend({'Full simulation', 'POD simulation'}) end



