vemmech: Mechanics for general grids using the virtual element method

Geomechanics with the Virtual Element Method

The virtual element method (VEM) is a generalization of the finite element method to very general pologonal and polyhedral grids. The method takes inspiration from mimetic methods (see the ‘mimetic’ module) and is designed to fulfill the patch test, i.e., reproduce exact solutions for displacements that vary linearly in space (strain solutions).

The module contains a preliminary implementation of VEM for linear elasticity and poro eleasticity, and includes several examples with grids combining tetrahedal, hexahedral, and general polygonal elements. The tutorial examples do not offer much explaination or discussion of results, and the computational routines are not yet fully documented. Despite this, we have decided to publish the module ahead of time after several people have asked us to do so.

Contents

UTILS

Files
addFluidContribMechVEM - SYNOPSIS: C2D - Compute the matrix of normalized strain energies (D) from the elasticity calStressEigsVEM - calculate eigen values of stress tensor and the basis calculateQC - SYNOPSIS: calculateQF - SYNOPSIS: calculateStressVEM - Undocumented Utility Function complex3DGrid - SYNOPSIS: Enu2C - For each cell, construct the 3x3 (in 2D) or 6x6 (in 3D) matrix representing ENu2LMu_3D - SYNOPSIS: exploreSquareGrid - Explore the different types of grids that can be set up using the function squareGrid lincompTPFA - Solve weakly compressible flow problem (fluxes/pressures) using TPFA method. linearDisplacement - SYNOPSIS: squareGrid - SYNOPSIS: squareLayersTest - SYNOPSIS: squareTest - Different test cases for linear elasticity on square domains VEM2D_div - Discrete div operator for the virtual element method in 2D VEM3D_div - Discrete div operator for the virtual element method in 3D VEM_div - Discrete div operator for the virtual element method
exploreSquareGrid

Explore the different types of grids that can be set up using the function squareGrid

C2D(C, G, varargin)

Compute the matrix of normalized strain energies (D) from the elasticity tensor (C) associated with grid (G).

convert Stiffness tensor from Voigt notation to plain notations (no factors are applied to the off-diagonal coefficients of the strain tensor). The latter is used in our formulation of VEM. The resulting tensor D is such that, for S in plain notation, we have: S’DS = energy.

ENu2LMu_3D(E, nu)

Synopsis:

function [lambda, mu] = ENu2LMu_3D(E, nu)

DESCRIPTION: Compute the Lamé parameters lambda and mu in the 2D case from the Young’s modulus E and Poisson’s ratio Nu. In the orthogonal direction (with respect to the 2D plane), it is assumed zero displacement. In particular, it implies that Nu = 0.5 corresponds as in the 3D case to an incompressible material. An other assumption, which is not used here, could be that there is no stress in the orthogonal direction.

Parameters:
  • E – Young’s modulus
  • nu – Poisson’s ratio
Returns:
  • lambda – First Lamé coefficient
  • mu – Second Lamé coefficient

EXAMPLE:

SEE ALSO:

Enu2C(E, nu, G)

For each cell, construct the 3x3 (in 2D) or 6x6 (in 3D) matrix representing the elasticity tensor.

Synopsis:

function C = Enu2C(E, nu, G)

DESCRIPTION:

In 3D, the matrix of the elasticity tensor for a given cell is written:

1-nu nu nu 0 0 0 |
nu 1-nu nu 0 0 0 |
nu nu 1-nu 0 0 0 | E
0 0 0 (1-2nu)/2 0 0 | x ————–
0 0 0 0 (1-2nu)/2 0 | (1+nu) (1-2nu)
0 0 0 0 0 (1-2nu)/2 |
In 3D, the inverse of the elasticity tensor is given by
[ [ 1 / E, -(nu / E), -(nu / E), 0, 0, 0 ]
[ -(nu / E), 1 / E, -(nu / E), 0, 0, 0 ] [ -(nu / E), -(nu / E), 1 / E, 0, 0, 0 ] [ 0, 0, 0, 2*(nu + 1) / E, 0, 0 ] [ 0, 0, 0, 0, 2*(nu + 1) / E, 0 ] [ 0, 0, 0, 0, 0, 2*(nu + 1) / E ] ]
In 2D, the inverse is given
[ [ 1 - nu, -nu, 0 ] 1 + nu
[ -nu, 1 - nu, 0 ] x ————– [ 0, 0, 2 ] ] E

The factors 2 in the expressions of the inverse above are due to Voigts notations, see https://en.wikipedia.org/wiki/Voigt_notation

invCi = C^{-1}*(Identity tensor)

Parameters:
  • E – Young’s modulus (one entry per cell)
  • nu – Poisson ratio (one entry per cell)
  • G – Grid
Returns:

C – (k,n) matrix, where k=3^2 (2D) or k=6^2 (3D), and n is the number of cells. Each row thus represents the entries of the elasticity tensor for a specific cell.

VEM2D_div(G, varargin)

Discrete div operator for the virtual element method in 2D

Synopsis:

function [div] = VEM2D_div(G)

DESCRIPTION: Computes a discrete div operator in 2D. This discrete div operator is a mapping from node-valued displacement vector to cell-valued 2D vector. Node-valued displacement vectors correspond to the degrees of freedom that determine for each cell a displacement function over the cell via the virtual basis function. The discrete div operator that is assembled here computes the L^2 projection, cell-wise, of this displacement function. For more detail, see paper [Andersen et al: http://arxiv.org/abs/1606.09508v1].

Parameters:G – Grid structure
Keyword Arguments:
 ‘extraFaceDof’ – This option has to be set to get a stable divergence operator when extra degrees of freedom have been introduced on the edges to avoid numerical locking.
Returns:div – matrix corresponding to the discrete div operator.

EXAMPLE:

SEE ALSO:

VEM3D_div(G)

Discrete div operator for the virtual element method in 3D

Synopsis:

function [div] = VEM3D_div(G)

DESCRIPTION: Computes a discrete div operator in 3D. This discrete div operator is a mapping from node-valued displacement vector to cell-valued 2D vector. Node-valued displacement vectors correspond to the degrees of freedom that determine for each cell a displacement function over the cell via the virtual basis function. The discrete div operator that is assembled here computes the L^2 projection, cell-wise, of this displacement function. For more detail, see paper [Andersen et al: http://arxiv.org/abs/1606.09508v1].

Parameters:G – Grid structure
Returns:div – matrix corresponding to the discrete div operator.

EXAMPLE:

SEE ALSO:

VEM_div(G)

Discrete div operator for the virtual element method

Synopsis:

function [div] = VEM_div(G)

DESCRIPTION: Computes a discrete div operator. This discrete div operator is a mapping from node-valued displacement vector to cell-valued vector. Node-valued displacement vectors correspond to the degrees of freedom that determine for each cell a displacement function over the cell via the virtual basis function. The discrete div operator that is assembled here computes the L^2 projection, cell-wise, of this displacement function. For more detail, see paper [Andersen et al: http://arxiv.org/abs/1606.09508v1].

Parameters:G – Grid struture
Returns:div – matrix corresponding to the discrete div operator.

EXAMPLE:

SEE ALSO:

addFluidContribMechVEM(G, bc, rock, isdirdofs)

Synopsis:

function fbc = addFluidContribMechVEM(G, bc, rock, isdirdofs)

DESCRIPTION: Setup the force boundary condition for the mechanics corresponding to the fluid boundary condition.

Parameters:
  • G – Grid structure
  • bc – Fluid boundary conditions.
  • rock – Rock structure
  • isdirdofs – Degrees of freedom where in fact Dirichlet boundary conditions are imposed
Returns:

fbc – Force load for the mechanical system.

EXAMPLE:

SEE ALSO:

calStressEigsVEM(G, stress, varargin)

calculate eigen values of stress tensor and the basis

transform to traditional voits notation (for stress this has no factors)

calculateQC(G)

Synopsis:

function [qc, qf, qcvol] = calculateQC(G)

DESCRIPTION: Calculate elementary integrals that are used to assemble the stiffness matrix for the 3D case. The precise definitions can be found in [Gain et al: doi:10.1016/j.cma.2014.05.005].

Parameters:

G – Grid structure

Returns:
  • qc – Elementary assembly integrals : One (3D) vector value in each cell, see (74) in [Gain et al].
  • qf – Elementary assembly integrals : One scalar value for each face for each node, corresponds to (98) in [Gain et al].
  • qcvol – Elementary assembly integrals : One scalar value for each node in each cells, gives weights to compute the L^2 projections, see VEM_linElast.m

EXAMPLE:

SEE ALSO:

calculateQF(G)

Synopsis:

function [qf, qf_vol] = calculateQF(G)

DESCRIPTION: Calculate elementary integrals that are used to assemble the stiffness matrix for the 2D case.

Parameters:

G – Grid structure

Returns:
  • qf – Elementary assembly integrals : One (2D) vector value in each cell, which corresponds to the two components of the integral of the basis function in each coordinate over the faces (see (74) in [Gain et al: doi:10.1016/j.cma.2014.05.005], then faces there correspond to edges here).
  • qf_vol – Elementary assembly integrals : one scalar value for each node, wich corresponds to the weights that are used to compute th L^2 projection, see VEM_linElast.m

EXAMPLE:

SEE ALSO:

calculateStressVEM(G, uu, op, varargin)

Undocumented Utility Function

complex3DGrid(opt, grid_case)

Synopsis:

function [G, G_org] = complex3DGrid(opt, grid_case)

DESCRIPTION:

Parameters:
  • opt

    Parameter structure with fields L - Physical dimension (‘box’ and ‘grdecl’ case) cartDims - Cartesian dimension (‘box’ and ‘grdecl’ case) disturb - disturbance parameter for the grid (‘box’ case) triangulate - If true, the horizontal faces are triangulated. vertical - Only relevant for Norne grid (straightens up

    the pillars, see paper [Andersen et al: http://arxiv.org/abs/1606.09508v1])
    gtol - Grid tolerance parameter (used when calling
    processGRDECL, see documentation there)

    ref - Refinement parameter, only used for Norne grid

  • grid_case – 3D Grid cases that are set up here. ‘box’ - grid set up using squareGrid function ‘grdecl’ - grid set up using Eclipse format ‘sbed’ - run dataset_bedmodel2() to get a full description ‘Norne’ - run dataset_norne() to get a full description
Returns:
  • G – Grid that has been created
  • G_org – Original grid without refinement (only ‘Norne’ case)

Example:

G=complex3DGrid(struct('vertical',true,'triangulate',false,'ref',1,'gtol',1e-3),'norne');clf,plotGrid(G),view(3)
G=complex3DGrid(struct('triangulate',false,'gtol',1e-3),'sbed');clf,plotGrid(G),view(3)
G=complex3DGrid(struct('triangulate',true,'gtol',1e-3),'sbed');clf,plotGrid(G),view(3)
G=complex3DGrid(struct('triangulate',false,'gtol',1e-3),'sbed');clf,plotGrid(flipGrid(G)),view(3)
G=complex3DGrid(struct('cartDims',[3,3,3],'L',[3 3 3],'disturb',0.04,'triangulate',false),'box');clf,plotGrid(G),view(3)
G=complex3DGrid([],'grdecl'); clf, plotGrid(G), view(3)
lincompTPFA(dt, state, G, T, pv, fluid, rock, varargin)

Solve weakly compressible flow problem (fluxes/pressures) using TPFA method.

Synopsis:

state = lincompTPFA(state, G, T, fluid)
state = lincompTPFA(state, G, T, fluid, 'pn1', pv1, ...)

Description:

This function assembles and solves a (block) system of linear equations defining interface fluxes and cell 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).

This function uses a two-point flux approximation (TPFA) method with minimal memory consumption within the constraints of operating on a fully unstructured polyhedral grid structure.

Parameters:
  • state – Reservoir and well solution structure either properly initialized from functions ‘initResSol’ and ‘initWellSol’ respectively, or the results from a previous call to function ‘lincompTPFA’ and, possibly, a transport solver such as function ‘implicitTransport’.
  • T (G,) – Grid and half-transmissibilities as computed by the function ‘computeTrans’.
  • fluid – Fluid object as defined by function ‘initSimpleFluid’.
Keyword Arguments:
 
  • wells – Well structure as defined by function ‘addWell’. May be empty (i.e., W = struct([])) 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 = struct([])) 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 = struct([])) which is interpreted as a reservoir model without explicit sources.

  • 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 ‘lincompTPFA’. Logical. Default value: MatrixOutput = FALSE.

  • verbose – Enable output. Default value dependent upon global verbose settings of function ‘mrstVerbose’.

  • condition_number – Display estimated condition number of linear system.

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, 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

‘lincompTPFA:DrivingForce:Missing’

Example:

G   = computeGeometry(cartGrid([3, 3, 5]));

f   = initSingleFluid('mu' , 1*centi*poise, ...
                      'rho', 1000*kilogram/meter^3);
rock.perm = rand(G.cells.num, 1)*darcy()/100;

bc  = pside([], G, 'LEFT', 2*barsa);
src = addSource([], 1, 1);
W   = verticalWell([], G, rock, 1, G.cartDims(2), []   , ...
                   'Type', 'rate', 'Val', 1*meter^3/day, ...
                   'InnerProduct', 'ip_tpf');
W   = verticalWell(W, G, rock, G.cartDims(1), G.cartDims(2), [], ...
                   'Type', 'bhp', 'Val', 1*barsa, ...
                   'InnerProduct', 'ip_tpf');

T   = computeTrans(G, rock);

state         = initResSol (G, 10);
state.wellSol = initWellSol(G, 10);

state = lincompTPFA(state, G, T, f, 'bc', bc, 'src', src, ...
                   'wells', W, 'MatrixOutput', true);

plotCellData(G, state.pressure)
linearDisplacement(dim)

Synopsis:

function [disp, names] = linearDisplacement(dim)

DESCRIPTION:

Parameters:

dim – Dimension

Returns:
  • disp
  • names

EXAMPLE:

SEE ALSO:

squareGrid(cartDims, L, varargin)

Synopsis:

G = squareGrid(cartDims, L, varargin)

DESCRIPTON: Make square with different irregular grid types starting from cartesian.

run script exploreSquareGrid.m to see the different possibilities

Parameters:
  • cartDims – Cartesian dimensions
  • L – Physical lengths
Keyword Arguments:
 
  • grid_type – Possible types are ‘cartgrid’ ‘triangle’ ‘pebi’ ‘boxes[1-4]’ ‘mixed[1-4]’. Run the scripts given in the comments below to see how the grid look likes. Only the ‘cartgrid’ type is implemented for 3D. The others apply uniquely for 2D.
  • disturb – The grids can be twisted, essentially to break the Cartesian structure. The parameter disturb determines the amount of twisting that is introduced.
Returns:

G – Grid structure

squareLayersTest(varargin)

Synopsis:

function [G, bc, test_cases] = squareLayersTest(varargin)

DESCRIPTION: make test cases for linear elasticity with several layers

Parameters:

varargin

Returns:
  • G
  • bc
  • test_cases
squareTest(varargin)

Different test cases for linear elasticity on square domains

Synopsis:

function [G, bc, test_cases] = squareTest(varargin)

DESCRIPTION: Set up different test cases to test the linear elasticity code.

Keyword Arguments:
 
  • ‘L’ – Physical dimension
  • ‘cartDims’ – Cartesian dimension
  • ‘grid_type’ – grid type (see squareGrid function)
  • ‘disturb’ – Disturbance coefficient
  • ‘E’ – Young’s modulus
  • ‘nu’ – Poisson’s ratio
  • ‘make_testcases’ – If true, compute linear displacement test cases.
  • ‘test_name’ – test case name. The alternatives are ‘original_2D_test’ ‘hanging_rod_2D’ ‘weighted_slop_rod_2D_dir’ ‘weighted_slop_rod_2D’ ‘weighted_slop_rod_2D_nobcleftright’ ‘rod_2D_pressure’ ‘rod_2D_pressure_nobcleftright’ ‘rod_2D_point_pressure’ ‘rod_2D_point_pressure_noleftrighbc’ ‘all’
Returns:
  • G – Grid structure
  • bc – Boundary conditions
  • test_cases – Cell structure with the test cases
Contents

GRID_UTILS

Files
flipGrid - Flip a grid (z->x, x->y, y->z) padGrdecl - Add padding to corner-point grid so it is embedded in a box verticalGrdecl - Transform GRDECL pillars into vertical pillars
flipGrid(G)

Flip a grid (z->x, x->y, y->z)

Synopsis:

function G_new = flipGrid(G);

DESCRIPTION: Flip the grid (z->x, x->y, y->z)

Parameters:G – Grid structure
Returns:G_new – Grid structure afte flipping
padGrdecl(grdecl, dirs, box, varargin)

Add padding to corner-point grid so it is embedded in a box

Synopsis:

function grdecl_new = padGrdecl(grdecl, dirs, box, varargin)

DESCRIPTION: Adds padding on all the sides of a corner point grid in order to make it a Cartesian box

Parameters:
  • grdecl – Grid in eclipse format
  • dirs – Padding directions
  • box – Size of elements in each padding layer
Keyword Arguments:
 

relative

Returns:

grdecl_new – Grid in eclipse format, with padding.

verticalGrdecl(grdecl, varargin)

Transform GRDECL pillars into vertical pillars

Synopsis:

function grdecl = verticalGrdecl(grdecl, varargin)

DESCRIPTION: Straightened up the pillars in a corner point grid, given in Eclipse, and make them vertical

Parameters:

grdecl – Grid structure in Eclipse format

Keyword Arguments:
 

method – ‘all’ : all pillars are made vertical

‘sides’ : Only the pillare on the sides are made vertical

Returns:

grdecl – Grid structure with vertical pillars in Eclipse format.

EXAMPLE: SEE ALSO:

Contents

MODELS

Files
LinearCompressibilityModel -
Contents

PLOTTING

Files
plotCellDataDeformed - SYNOPSIS: plotFaceDataDeformed - SYNOPSIS: plotFaces2D - Undocumented Utility Function plotGridDeformed - SYNOPSIS: plotNodeDataDeformed - SYNOPSIS:
plotCellDataDeformed(G, data, u, varargin)

Synopsis:

function h = plotCellDataDeformed(G, data, u, varargin)

DESCRIPTION: Plot cell data on a grid which is deformed using a given displacement field.

Parameters:
  • G – Grid structure
  • data – Data to be plotted
  • u – Displacement field
  • varargin – Optional parameters that are passed further to the function plotCellData
Returns:

h – handle to plot

EXAMPLE:

SEE ALSO: plotCellData

plotFaceDataDeformed(G, data, u, varargin)

Synopsis:

function h = plotFaceDataDeformed(G, data, u, varargin)

DESCRIPTION: Plot face data on a grid which is deformed using a given displacement field.

Parameters:
  • G – Grid structure
  • data – Data to be plotted
  • u – Displacement field
  • varargin – Optional parameters that are passed further to the function plotFaceData
Returns:

h – handle to plot

EXAMPLE:

SEE ALSO: plotFaceData

plotFaces2D(G, faces, varargin)

Undocumented Utility Function

plotGridDeformed(G, u, varargin)

Synopsis:

function h = plotGridDeformed(G, u, varargin)

DESCRIPTION: plot a deformed grid using a given displacement field

Parameters:
  • G – Grid structure
  • u – Displacement field
  • varargin – Optional parameters that are passed further to the function plotCellData
Returns:

h – handle to plot

EXAMPLE:

SEE ALSO:

plotNodeDataDeformed(G, node_data, uu, varargin)

Synopsis:

plotNodeDataDeformed(G, node_data, uu, varargin)

DESCRIPTION: Plot node data on a grid which is deformed using a given displacement field.

Parameters:
  • G – Grid data structure.
  • node_data – node data to be plotted
  • 'pn'/pv – List of other property name/value pairs. OPTIONAL. This list will be passed directly on to function PATCH meaning all properties supported by PATCH are valid.
Returns:

h – Handle to resulting PATCH object. The patch object is added to the current AXES object.

EXAMPLE:

See also

plotCellData, plotGrid, newplot, patch, shading.

Examples

Compaction test case in 2D

Generated from compactionTest2D.m

mrstModule add vemmech

Define the fluid and rock parameters and set up the grid.

opt = struct('grid_type', 'cartgrid', ...
% see squareGrid
            'disturb'   , 0.0, ...
     % parameter for disturbing grid
            'E'         , 0.3*1e9, ...
 % young's modulus
            'nu'        , 0.4, ...
            'islinear'  , false);       % poisson's ratio

opt.cartDims       = [10 10];
opt.L              = [15*10/10 15];
opt.hanging        = false;
opt.free_top       = true; % If true, the nodes at the top can move freely (no
                           % boundary condition given there)
opt.triangulate    = true; % If true, the horizontal faces are triangulated.
opt.gravity_load   = true; % Use gravity load
opt.top_load       = false;
opt.gtol           = 0.1e-1; % Grid tolerance parameter (used when calling
                             % processGRDECL, see documentation there)
opt.twist          = true;
opt.disturb        = 0.05;
% Different methods are implemented to compute the loading term, see
% paper [Andersen et al: http://arxiv.org/abs/1606.09508v1].
opt.force_method   = 'cell_force'; % 'dual_grad_type'

G = squareGrid(opt.cartDims, opt.L, 'grid_type', opt.grid_type);

if(opt.triangulate)
    face = unique(G.cells.faces(any(bsxfun(@eq, G.cells.faces(:, 2), [3, 4]), 2), 1));
    G = triangulateFaces(G, face);
    G = sortEdges(G);
end

if (opt.twist)
    G = twister(G, opt.disturb);
end

G = createAugmentedGrid(G);
G = computeGeometry(G);

Ev  = repmat(opt.E, G.cells.num, 1);
nuv = repmat(opt.nu, G.cells.num, 1);
C   = Enu2C(Ev, nuv, G);

figure()
clf,
plotGrid(G, 'Marker', '*')
title('Grid')
_images/compactionTest2D_01.png

Setup the loads and the boundary conditions

% We use the utility function makeCompactionTest
[el_bc, load] = makeCompactionTest(G, opt);

Assemble and solve the system

bbsize = 30000-(G.griddim-2)*20000; % block size for the assembly
uu = VEM_linElast(G, C, el_bc, load, ...
                  'blocksize'   , bbsize, ...
                  'force_method', opt.force_method);

figure(),
clf
plotNodeDataDeformed(G, uu(:, G.griddim), uu);
title('Displacement in the vertical direction')
_images/compactionTest2D_02.png

Computation of the analytical solution

if(~isempty(el_bc.force_bc))
    ff = abs(el_bc.force_bc.force(1, G.griddim));
else
    ff = 0;
end
start = max(G.faces.centroids(:, G.griddim));
top   = min(G.faces.centroids(:, G.griddim));

fac = 100*300/2;

ana = @(z) ff*(z-start)./(C(1, 1))-double(opt.gravity_load)*fac*((top-start).^2 - (z-top).^2)/C(1, 1);
divana = @(z) (ff./C(1, 1))-double(opt.gravity_load)*fac*(-2*(z-top))/C(1, 1);

Comparison plots

z = G.nodes.coords(:, G.griddim);
z(abs(ana(z))<max(abs(ana(z)))*1e-2) = nan;
zl = unique(z);

figure(),
plot(z, uu(:, G.griddim), '*', zl, ana(zl))
title('Displacement in the vertical direction')
legend({'computed solution', 'analytical solution'})

figure()
zc = G.cells.centroids(:, G.griddim);
div = VEM_div(G);
plot(zc, div*reshape(uu', [], 1)./G.cells.volumes, '*', zc, divana(zc));
title('Divergence');
legend({'computed solution', 'analytical solution'})
_images/compactionTest2D_03.png
_images/compactionTest2D_04.png

Compaction test case 3D

Generated from compactionTest3D.m

Load required modules

mrstModule add vemmech

Define the fluid and rock parameters and set up the grid.

opt = struct('grid_type' , 'triangle', ...
             'disturb'   , 0.0, ...
    % parameter for  grid distortion
             'E'         , 0.3*1e9, ...
% Young's modulus
             'nu'        , 0.4, ...
    % Poison's ratio
             'islinear'  , false);
opt.L            = [15 15 3];
opt.islinear     = false; % if true, the boundary condition is a given linear
                          % displacement, see function makeCompactionTest.
% Different methods are implemented to compute the loading term, see
% paper [Andersen et al: http://arxiv.org/abs/1606.09508v1].
opt.force_method = 'dual_grad_type';
opt.hanging      = false; % If true, zero displacement on the vertical sides.
opt.free_top     = true;  % If true, the nodes at the top can move freely (no
                          % boundary condition given there)
opt.triangulate  = false;  % If true, the horizontal faces are triangulated.
opt.vertical     = false; % Only relevant for norne test case (straightens up
                          % the pillars, see paper [Andersen et al: http://arxiv.org/abs/1606.09508v1])
opt.gravity_load = true;  % Use gravity load
opt.top_load     = true;  % Use force applied at the top
opt.gtol         = 0.1e-1; % Grid tolerance parameter (used when calling
                           % processGRDECL, see documentation there)
opt.ref          = 10;     % Refinement parameter, only used for Norne
opt.flipgrid     = false;  % Rotate the grid (z->x, x->y, y->z) (see paper [Andersen et al: http://arxiv.org/abs/1606.09508v1])
if isempty(mfilename)
    % Running example interactively
    grid_case_number = input(['Choose a grid (type corresponding number): box [1], ' ...
                              'sbed [2], Norne [3]\n']);
else
    % Running example as a function
    grid_case_number = 1;
end
switch grid_case_number
  case 1
    grid_case = 'box';
    opt.cartDims     = [[1 1]*3 10]; % set the Cartesian dimension for the box case
  case 2
    grid_case = 'sbed';
  case 3
    grid_case = 'norne';
  otherwise
    error('Choose grid case by typing number between 1 and 3.');
end

G = complex3DGrid(opt, grid_case);

if (opt.flipgrid)
    G = flipGrid(G);
end
G = createAugmentedGrid(G);
G = computeGeometry(G);

Ev     = repmat(opt.E, G.cells.num, 1);
nuv    = repmat(opt.nu, G.cells.num, 1);
C      = Enu2C(Ev, nuv, G);

figure()
clf;
plotGrid(G);
view(3);
_images/compactionTest3D_01.png

Setup the loads and the boundary conditions

if(strcmp(grid_case,'norne'))
    %only rolling in vertical direction this is need since norne has
    %irregular sides and the code do not have genneral implementation of
    %rolling condition at this point
    [el_bc, load] = makeCompactionTest(G, opt, 'rolling_vertical', true);
else
    [el_bc, load] = makeCompactionTest(G, opt);
end

Assemble and solve the system

bbsize = 30000-(G.griddim-2)*20000;
lsolve = @mldivide;
fprintf('Running ...
');
uu = VEM_linElast(G, C, el_bc, load, 'linsolve', lsolve, 'blocksize', bbsize, ...
                  'force_method', opt.force_method);
fprintf('done!\n');

figure();
clf;
plotNodeDataDeformed(G, uu(:, 3), uu);
view(3);
title('Vertical displacement')
Running ... done!
_images/compactionTest3D_02.png

Compute the analytical solution

ff = abs(el_bc.force_bc.force(1, 3));
start = max(G.faces.centroids(:, 3));
top = min(G.faces.centroids(:, 3));
[lambda, mu] = ENu2LMu_3D(opt.E, opt.nu);
gfac = 10*3000/2; % gravity is 10, density is 3000, 2 is because of derivative
ana = @(z) ff*(z-start)./(C(1, 1))-double(opt.gravity_load)*gfac*((top-start).^2 - (z-top).^2)/C(1, 1);
divana = @(z) (ff./C(1, 1))-double(opt.gravity_load)*gfac*(-2*(z-top))/C(1, 1);

Comparison plots

z = G.nodes.coords(:, G.griddim);
z(abs(ana(z))<max(abs(ana(z)))*1e-2) = nan;
zl = unique(z);

figure(),
plot(z, uu(:, G.griddim), '*', zl, ana(zl))
title('Displacement in the vertical direction')
legend({'computed solution', 'analytical solution'})

figure()
zc = G.cells.centroids(:, G.griddim);
div = VEM_div(G);
plot(zc, div*reshape(uu', [], 1)./G.cells.volumes, '*', zc, divana(zc));
title('Divergence');
legend({'computed solution', 'analytical solution'})
_images/compactionTest3D_03.png
_images/compactionTest3D_04.png

Linear Elasticity on Complex Grid

Generated from example_2D.m

We consider a case with a complex block-structured grid, in which each individual block is gridded using different grid types: curvilinear quadrilateral blocks, unstructured Voronoi grids, and triangular grids. The purpose of the example is to highlight the flexibility of the virtual element method.

mrstModule add vemmech

Set up the rock parameters and the grid

E  = 1;    % Young's modulus
nu = 0.3;  % Poisson's ratio

L = [1 1]; % Grid domain
dims = [16 16]; % The grid size

% Parameter used to twist the grid so that the Cartesian symmetries are
% broken and we can test the method on more irregular grid.
disturb = 0.03;

test_name = 'original_2D_test'; % list of test names to be found in file
                                % squareTest.
grid_type = 'mixed4'; % Run the script exploreSquareGrid to see the grids
                      % that have been set up to test this VEM code with
                      % respect to different grid features.

% We use the utility function squareTest to set up the problem, that is
% defining the load and boundary conditions.
[G, bc, test_cases] = squareTest('E', E, 'nu', nu, 'cartDims', dims, ...
                                 'L', L, 'grid_type', grid_type, 'disturb', disturb, ...
                                 'make_testcases', false, 'test_name', test_name);
G = computeGeometry(G);
clf, plotGrid(G);

% We recover the problem parameters using the structure test_cases
testcase = test_cases{1};
el_bc    = testcase.el_bc; % The boundary conditions
load     = testcase.load;  % The load
C        = testcase.C;     % The material parameters in Voigts notations
_images/example_2D_01.png

We assemble and solve the equations

[uVEM, extra] = VEM_linElast(G, C, el_bc, load);

We plot the results

plotopts = {'EdgeAlpha', 0.0, 'EdgeColor', 'none'};
plotopts1 = {'EdgeAlpha', 0.1};
figure('Position',get(0, 'DefaultFigurePosition').*[1 1 1 2]);
subplot(3, 1, 1);
plotNodeData(G, uVEM(:, 1), plotopts{:}); colorbar();
title('x displacement');
subplot(3, 1, 2);
plotNodeData(G, uVEM(:, 2), plotopts{:}); colorbar();
title('y displacement');
subplot(3, 1, 3);
% We compute the divergence
vdiv = VEM2D_div(G);
mdiv = vdiv*reshape(uVEM', [], 1)./G.cells.volumes;
title('Divergence');
plotCellDataDeformed(G, mdiv, uVEM, plotopts1{:}); colorbar();
_images/example_2D_02.png

A short example solving linear elasticity on a complex grids

Generated from example_2D_complex_grid.m

Load required modules

mrstModule add vemmech

Define parameters

opt = struct('L'         , [1 1], ...
             'cartDims'  , [4 4], ...
             'grid_type' , 'square', ...
             'disturb'   , 0.02, ...
%parameter for disturbing grid
             'E'         , 4e7, ...
 %youngs modolo
             'nu'        , 0.44);% poiso ratio

make a mixed type of grid

G = squareGrid(opt.cartDims, opt.L, 'grid_type', 'mixed4', 'disturb', opt.disturb);
G = removeCells(G, [140 : 151, 233 : 235, 249 : 250, 93, 117 : 118]');
G = createAugmentedGrid(G);
G = computeGeometry(G);
figure()
plotGrid(G);
title('Original grid');
_images/example_2D_complex_grid_01.png

Find sides of domain

[Lx, Ly] = deal(opt.L(1), opt.L(2));
assert(G.griddim == 2);
x = [0, Lx];
bc = cell(4,1);
for i = 1 : 2
    faces = find(abs(G.faces.centroids(:, 1) - x(i)) < eps);
    bc{i} = addBC([], faces, 'pressure', 0);
    bc{i} = rmfield(bc{i}, 'type');
    bc{i} = rmfield(bc{i}, 'sat');
end
y = [0, Ly];
for i = 1 : 2
    faces = find(abs(G.faces.centroids(:, 2) - y(i)) < eps);
    bc{i + 2} = addBC([], faces, 'pressure', 0);
    bc{i + 2} = rmfield(bc{i + 2}, 'type');
    bc{i + 2} = rmfield(bc{i + 2}, 'sat');
end

for i = 1 : 4
    inodes = mcolon(G.faces.nodePos(bc{i}.face), G.faces.nodePos(bc{i}.face + 1) - 1);
    nodes = unique(G.faces.nodes(inodes));
    % Set up the boudary conditions.
    % Note that the fields 'faces' and 'uu_face' are not needed for VEM but
    % will become necessary when running other methods (such as MPSA)
    disp_bc = struct('nodes', nodes, ...
        'uu', 0, ...
        'faces', bc{i}.face, ...
        'uu_face', 0, ...
        'mask', true(numel(nodes), G.griddim));
    bc{i}.el_bc = struct('disp_bc', disp_bc, 'force_bc', []);
end

Set up the loading term

Gravity is our loading term

density = 3000; %kg/m^3
grav = 10; %gravity
load = @(x) - (grav*density)*repmat([0, 1], size(x, 1), 1);

Set up the displacement boundary condtions

% Set boundary displacement function to zero
bcdisp = @(x) x*0.0;

% Set up the boundary conditions for each side
bc_el_sides{1} = bc{1}; % x side is fixed
bc_el_sides{2} = bc{2}; % y side is fixed
bc_el_sides{3} = [];    % bottom  is free
bc_el_sides{4} = [];    % top is free

% Collect the displacement boundary conditions
nodes = [];
faces = [];
mask  = [];
for i = 1 : numel(bc)
    if (~isempty(bc_el_sides{i}))
        nodes = [nodes; bc_el_sides{i}.el_bc.disp_bc.nodes]; %#ok
        faces = [faces; bc_el_sides{i}.el_bc.disp_bc.faces]; %#ok
        mask  = [mask; bc_el_sides{i}.el_bc.disp_bc.mask];   %#ok
    end
end
disp_node  = bcdisp(G.nodes.coords(nodes, :));
disp_faces = bcdisp(G.faces.centroids(faces, :));
disp_bc = struct('nodes', nodes, 'uu', disp_node, 'faces', faces, 'uu_face', disp_faces, 'mask', mask);

Set up the force boundary conditions

% A force is applied on the top surface. It is discontinuous in the sense
% that it takes two different values on the left and on the right.
force = 50*barsa;
face_force = @(x) force*sign(x(:, 1) - opt.L(1)/2) + 100*barsa;
faces = bc{4}.face;
% Set up the force boundary structure,
% Note that the unit for the  force is  Pa/m^3.
force_bc = struct('faces', faces, 'force', bsxfun(@times, face_force(G.faces.centroids(faces, :)), [0 -1]));


% Final structure defining the boundary conditions
el_bc = struct('disp_bc', disp_bc, 'force_bc', force_bc);

Define the rock parameters

Ev = repmat(opt.E, G.cells.num, 1);
nuv = repmat(opt.nu*0 + 0.4, G.cells.num, 1);
C = Enu2C(Ev, nuv, G);

Assemble and solve the system

lsolve = @mldivide;
[uu, extra] = VEM_linElast(G, C, el_bc, load, 'linsolve', lsolve);

Plot displacement in y direction

plotopts = {'EdgeAlpha', 0.0, 'EdgeColor', 'none'};
plotopts1 = {'EdgeAlpha', 0.01};
figure()
plotNodeData(G, uu(:, 2), plotopts{ : });
colorbar();
title('Displacement in the y direction');
_images/example_2D_complex_grid_02.png

Plot the displacement in x direction

figure()
plotNodeData(G, uu(:, 1), plotopts{ : });
title('Displacement in the x direction');
colorbar();
_images/example_2D_complex_grid_03.png

plot the deformed grid

figure()
fac = 1;
plotGridDeformed(G, uu*fac); axis tight
title('The deformed grid');
_images/example_2D_complex_grid_04.png

Calculate and plot the divergence

vdiv = VEM_div(G);
mdiv = vdiv*reshape(uu', [], 1)./G.cells.volumes;
figure()
plotCellDataDeformed(G, mdiv, uu*fac, plotopts1{ : }); axis tight
colorbar()
title('Divergence of the displacement field');
_images/example_2D_complex_grid_05.png

Calulate the stress and strain

op = extra;
stress = reshape(op.D*op.WC'*op.assemb'*reshape(uu', [], 1), 3, [])';

Example using three layers with different parameters in each layer

Generated from example_2D_layers.m

We set up a

mrstModule add vemmech

Set up the rock parameters and the grid

Use the utility function squareLayersTest to set up the problem. You may look at the documentation of this function and try the other settings presented there by modifying this example.

E  = [1; 10; 1];      % Define the Youngs moduli in the layers
nu = [0.3; 0.3; 0.3]; % Define the Poisson's ratio in the layers

zlayers = [0.3; 0.3; 0.3]; % Define the width of the layers.
zlayers = 1/sum(zlayers)*zlayers;
L    = [1 1];   % Define the domain size

% Define the grid type, for example 'mixed4'
grid_type = 'mixed4';
dims= [4 4];

% Define test case type. It determines the boundary conditions and the forces
test_name = 'hanging_rod_2D';
% Parameter used to twist the grid so that the Cartesian symmetries are
% broken and we can test the method on more irregular grid.
disturb = 0.042;

% The utility function squareLayersTest sets up the problem.
[G, bc, test_cases] = squareLayersTest('E', E, 'nu', nu, 'zlayers', ...
    zlayers, 'cartDims', dims, 'L', L, 'grid_type', grid_type, 'disturb', ...
    disturb, 'make_testcases', false, 'test_name', test_name);
G = computeGeometry(G);
figure,
plotGrid(G);

% We recover the problem parameters using the structure test_cases
testcase = test_cases{1};
el_bc    = testcase.el_bc; % The boundary conditions
load     = testcase.load;  % The load
C        = testcase.C;     % The material parameters in Voigts notations
_images/example_2D_layers_01.png

We assemble and solve the equations

[uVEM, extra] = VEM_linElast(G, C, el_bc, load);

We plot the results

plotopts = {'EdgeAlpha', 0.0, 'EdgeColor', 'none'};
plotopts1 = {'EdgeAlpha', 0.1};
figure('Position',get(0, 'DefaultFigurePosition').*[1 1 1 2]);
subplot(3, 1, 1);
plotNodeData(G, uVEM(:, 1), plotopts{:}); colorbar();
title('x displacement');
subplot(3, 1, 2);
plotNodeData(G, uVEM(:, 2), plotopts{:}); colorbar();
title('y displacement');
subplot(3, 1, 3);
% We compute the divergence
vdiv = VEM2D_div(G);
mdiv = vdiv*reshape(uVEM', [], 1)./G.cells.volumes;
title('Divergence');
plotCellDataDeformed(G, mdiv, uVEM, plotopts1{:}); colorbar(); axis tight
_images/example_2D_layers_02.png

Example of poro-elastic problem

Generated from example_poroelasticity.m

This example sets up a poro-elastic problem which mimics a slice of the overburden, with an infinite horizontal well in an aquifer at the bottom of the domain. The poro-elastic equations are set up together with linear explicitely. At the end we simulate the same case by using the poro-elastic solver and compare with the results predicted by linear elasticity. More examples can be generated by modifying the parameters sent to the function squareTest.m,

mrstModule add incomp vemmech

Define the grid and rock parameters

opt = struct( 'L'         , [10000 2000], ...
              'cartDims'  , [20 20]*1,    ...
              'grid_type' , 'square',     ...
              'disturb'   , 0.0,          ...
% Parameter for disturbing grid
              'E'         , 1e9,          ...
% Young's modulus
              'nu'        , 0.2);             % Poisson's ratio


G = cartGrid(opt.cartDims, opt.L);
if (opt.disturb > 0)
    G = twister(G, opt.disturb);
end
G = createAugmentedGrid(G);
G = computeGeometry(G);

Ev  = repmat(opt.E, G.cells.num, 1);
nuv = repmat(opt.nu, G.cells.num, 1);
C   = Enu2C(Ev, nuv, G);

figure()
clf,
plotGrid(G)
_images/example_poroelasticity_01.png

Set up gravity as the loading term

gravity reset on
density = 3000; % in kg/m^3
grav    = norm(gravity());   % gravity
load    = @(x) -(grav*density)*repmat([0, 1], size(x, 1), 1);

Set up the displacement boundary conditions

oside = {'Left', 'Right', 'Back', 'Front'};
bc = cell(4, 1);
for i = 1 : numel(oside);
    bc{i} = pside([], G, oside{i}, 0);
    bc{i} = rmfield(bc{i}, 'type');
    bc{i} = rmfield(bc{i}, 'sat');
end
% Find the nodes for the different sides and set the boundaray conditions for
% elastisity.
for i = 1 : 4
    inodes = mcolon(G.faces.nodePos(bc{i}.face), G.faces.nodePos(bc{i}.face + 1) - 1);
    nodes = unique(G.faces.nodes(inodes));
    disp_bc = struct('nodes'   , nodes,      ...
                     'uu'      , 0,          ...
                     'faces'   , bc{i}.face, ...
                     'uu_face' , 0,          ...
                     'mask'    , true(numel(nodes), G.griddim));
    bc{i}.el_bc = struct('disp_bc', disp_bc, 'force_bc', []);
end
bcdisp = @(x) x*0.0; % Boundary displacement function set to zero.
% Setup diriclet boundary conditions at selected sides
%
% On the left and right-hand sides, zero displacement is imposed in the x
% direction, and free in the y direction. This is done by using masks.
bc_el_sides{1} = bc{1};
bc_el_sides{1}.el_bc.disp_bc.mask(:, 2) = false;
bc_el_sides{2} = bc{2};
bc_el_sides{2}.el_bc.disp_bc.mask(:, 2) = false;
% Zero displacement is imposed in the y direction at the bottom, free
% displacement at the top.
bc_el_sides{3} = bc{3};
bc_el_sides{3}.el_bc.disp_bc.mask(:, 1) = false;
bc_el_sides{4} = [];

% Collect the displacement boundary conditions
nodes = [];
faces = [];
mask = [];
for i = 1 : numel(bc)
    if(~isempty(bc_el_sides{i}))
        nodes = [nodes; bc_el_sides{i}.el_bc.disp_bc.nodes]; %#ok
        faces = [faces; bc_el_sides{i}.el_bc.disp_bc.faces]; %#ok
        mask  = [mask; bc_el_sides{i}.el_bc.disp_bc.mask]; %#ok
    end
end
disp_node = bcdisp(G.nodes.coords(nodes, :));
disp_faces = bcdisp(G.faces.centroids(faces, :));
disp_bc = struct('nodes', nodes, 'uu', disp_node, 'faces', faces, 'uu_face', disp_faces, 'mask', mask);

Set up the force boundary conditions

% A force is applied on the top surface.
sigma = opt.L(2)/10;
force = 100*barsa;
face_force = @(x) force*exp(-(((x(:, 1) - opt.L(1)/2))./sigma).^2) + 10*barsa;
faces = bc{4}.face;
% Set up the force boundary structure
% Note that the unit for the force is in units Pa/m^3
force_bc = struct('faces', faces, 'force', bsxfun(@times, face_force(G.faces.centroids(faces, :)), [0 -1]));


% Final structure for the  boundary conditions
el_bc = struct('disp_bc', disp_bc, 'force_bc', force_bc);

Assemble and solve the system

[uu, extra] = VEM_linElast(G, C, el_bc, load);

% We retrieve the discrete operators that have been assembled
As        = extra.disc.A;     % Global stiffness matrix
div       = extra.disc.div;   % Divergence operator
gradP     = extra.disc.gradP; % Gradient operator (dual of div)
isdirdofs = extra.disc.isdirdofs; % Degrees of freedom where in fact
                                  % Dirichlet boundary conditions are imposed.
rhs_s     = extra.disc.rhs;   % Right-hand side
Vdir      = extra.disc.V_dir; %
ind_s     = [1 : size(As, 1)]';%#ok

Set up the parameters for the flow part

perm = 1*darcy*ones(G.cartDims);
perm(:, floor(G.cartDims(2)/5) : end) = 1*milli*darcy;
rock = struct('perm', reshape(perm, [], 1),     ...
              'poro', 0.1*ones(G.cells.num, 1), ...
              'alpha', ones(G.cells.num, 1)); % Rock parameters
fluid = initSingleFluid('mu', 1*centi*poise, 'rho', 1000); % Fluid parameters
rock.cr = 1e-4/barsa; % Fluid compressibility
T = computeTrans(G, rock); % Compute the transmissibilities
pv = poreVolume(G, rock); % Compute the pore volume
pressure = 100*barsa*ones(G.cells.num, 1); % Initial pressure
% Set up the initial state for the flow.
state = struct('pressure', pressure, 's', ones(G.cells.num, 1), 'flux', zeros(G.faces.num, 1));
% Set up a well structure for the fluid injection and also boundary
% conditions for the flow.
mcoord = [5000 200];
[dd, wc] = min(sum(bsxfun(@minus, G.cells.centroids, mcoord).^2, 2));
W = addWell([], G, rock, wc, 'type', 'bhp', 'val', 3000*barsa);
bc_f = pside([], G, 'Front', 10*barsa);
fbc = addFluidContribMechVEM(G, bc_f, rock, isdirdofs);

Set up matrix for mechanics and flow

Use a TPFA solver for weakly compressible flow. From the output we retrieve the operators for the fluid.

dt = day; % time step.
state = lincompTPFA(dt, state, G, T, pv, fluid, rock, 'MatrixOutput', true, 'wells', W, 'bc', bc_f);

Af    = state.A;   % Global matrix for the flow solver
orhsf = state.rhs; % Right-hand side for the flow equations (includes well)
ct    = state.ct;  % Compressibility coefficient
ind_f = [ind_s(end) + 1 : ind_s(end) + G.cells.num]';%#ok

% x denotes the global unknown (displacement + pressure)
x = zeros(ind_f(end), 1);
x(ind_f) = pressure;
p = pressure;

uu0 = uu;            % Solution from linear elasticity
u_tmp = reshape(uu', [], 1);
x(1 : ind_s(end)) = u_tmp(~isdirdofs);
u = zeros(numel(isdirdofs), 1);
rhsf = zeros(size(orhsf));
plotops = {'EdgeColor', 'none'};
fac = rock.poro(1);

% Assemble the matrix for the global system, mechanics + flow
zeromat = sparse(size(Af, 1) - G.cells.num, size(div, 2));
SS = [As, [fac*(-gradP), zeromat']; [fac*div; zeromat], ct + dt*Af];

Time loop

Set up bigger figures

df = get(0, 'DefaultFigurePosition');
figure(1); set(1, 'Position', df.*[0.8, 1, 3, 1]); clf
figure(2); set(2, 'Position', df.*[1, 1, 3, 1]); clf

t = 0;
end_time = 10*dt;
while t < end_time
    t = t + dt;
    % We update the right-hand side for the fluid (divergence term comes
    % from mechanics).
    rhsf(1 : G.cells.num) = orhsf(1 : G.cells.num)*dt + ct(1 : G.cells.num, 1 : G.cells.num)*p + fac*div*x(ind_s);
    rhsf(G.cells.num + 1 : end) = orhsf(G.cells.num + 1 : end);
    rhs = [rhs_s - fbc; rhsf];

    % Solve the system
    x = SS\rhs;

    % Retrieve pressure and displacement
    p = x(ind_f); % retrieve pressure
    u(isdirdofs) = Vdir(isdirdofs);
    u(~isdirdofs) = x(ind_s); % retrieve displacement
    uu = reshape(u, G.griddim, [])';

    % Plot absolute results from poromechanics
    figure(1)
    subplot(2, 2, 1), cla
    plotNodeData(G, uu(:, 1), plotops{ : }); colorbar;
    title('Displacement in x direction')
    subplot(2, 2, 2), cla
    plotNodeData(G, uu(:, 2), plotops{ : }); colorbar;
    title('Displacement in y direction')
    subplot(2, 2, 3), cla
    plotCellDataDeformed(G, p/barsa, uu); colorbar;
    title('Pressure')
    subplot(2, 2, 4), cla
    ovdiv = extra.disc.ovol_div;
    mdiv = ovdiv*reshape(uu', [], 1)./G.cells.volumes;
    plotCellDataDeformed(G, mdiv, uu); colorbar();
    title('Divergence')

    % Plot difference from results predicted by linear elasticity
    figure(2)
    uur = uu - uu0;
    subplot(2, 2, 1), cla
    plotNodeData(G, uur(:, 1), plotops{ : }); colorbar;
    title('Relative displacement in x direction')
    subplot(2, 2, 2), cla
    plotNodeData(G, uur(:, 2), plotops{ : }); colorbar;
    title('Relative displacement in y direction')
    subplot(2, 2, 3), cla
    plotCellDataDeformed(G, p/barsa, uur); colorbar;
    title('Pressure (grid deformed using relative disp.)')
    axis tight
    subplot(2, 2, 4), cla
    ovdiv = extra.disc.ovol_div;
    mdiv = ovdiv*reshape(uur', [], 1)./G.cells.volumes;
    plotCellDataDeformed(G, mdiv, uu); colorbar();
    title('Divergence of relative displacement')
    axis tight
    %pause(0.01);

end
_images/example_poroelasticity_02.png
_images/example_poroelasticity_03.png