Porous Flow Tutorial Page 00

Contents

  • Page 01 A single fluid

  • Page 02 Numerical issues

  • Page 03 Adding heat advection and conduction

  • Page 04 Adding solid mechanics

  • Page 05 Using a realistic equation of state for the fluid

  • Page 06 Adding a tracer

  • Page 07 A chemically reactive tracer with porosity and permeability changes

  • Page 08 The PorousFlowSink and unsaturated flow

  • Page 09 An overview of the PorousFlow architecture

  • Page 10 Unleashing the full power of PorousFlow: using Kernels and Materials

  • Page 11 Two-phase THM borehole injection

  • Page 12 Boundary sinks and sources, and polyline sinks and sources (boreholes and rivers)

  • Page 13 More elaborate chemistry

Introduction

The PorousFlow module is a library of physics for fluid and heat flow in porous media. It is formulated in a general manner, so is capable of solving problems with an arbitrary number of phases, fluid components and chemical reactants. This tutorial guides the user through some commonly-used aspects of PorousFlow. Lots of core documentation may be found in Porous Flow.

This tutorial concerns fluid injection through a borehole into a large fluid-filled reservoir. The borehole is vertical, and cylindrical symmetry around the borehole axis is assumed. The tutorial begins with simple Darcy flow, and gradually adds more complex phenomena such as coupling with heat and solid mechanics, multi-phase flows and chemical reactions.

After conceptual modelling, the first step in any finite-element simulation is to create the mesh. The 3D mesh used for this tutorial is deliberately coarse (so that the tutorial input files may be easily run on the smallest computers) and is generated using MOOSE's inbuilt meshing capabilities. Firstly, a 2D annular mesh is created:

[Mesh<<<{"href": "../../syntax/Mesh/index.html"}>>>]
  [annular]
    type = AnnularMeshGenerator<<<{"description": "For rmin>0: creates an annular mesh of QUAD4 elements. For rmin=0: creates a disc mesh of QUAD4 and TRI3 elements. Boundary sidesets are created at rmax and rmin, and given these names. If dmin!=0 and dmax!=360, a sector of an annulus or disc is created. In this case boundary sidesets are also created at dmin and dmax, and given these names", "href": "../../source/meshgenerators/AnnularMeshGenerator.html"}>>>
    nr<<<{"description": "Number of elements in the radial direction"}>>> = 10
    rmin<<<{"description": "Inner radius.  If rmin=0 then a disc mesh (with no central hole) will be created."}>>> = 1.0
    rmax<<<{"description": "Outer radius"}>>> = 10
    growth_r<<<{"description": "The ratio of radial sizes of successive rings of elements"}>>> = 1.4
    nt<<<{"description": "Number of elements in the angular direction"}>>> = 4
    dmin<<<{"description": "Minimum degree, measured in degrees anticlockwise from x axis"}>>> = 0
    dmax<<<{"description": "Maximum angle, measured in degrees anticlockwise from x axis. If dmin=0 and dmax=360 an annular mesh is created. Otherwise, only a sector of an annulus is created"}>>> = 90
  []
[]
(modules/porous_flow/examples/tutorial/00.i)

The radius of the borehole is 1m, the radius of the model is 10m, and only of the annulus is considered.

Now a sequence of modifiers are applied to this 2D mesh. First, it is extruded to be 12m high using:

[Mesh<<<{"href": "../../syntax/Mesh/index.html"}>>>]
  [make3D]
    type = AdvancedExtruderGenerator<<<{"description": "Extrudes a 1D mesh into 2D, or a 2D mesh into 3D, and supports a variable height for each elevation, variable number of layers within each elevation, variable growth factors of axial element sizes within each elevation and remap subdomain_ids, boundary_ids and element extra integers within each elevation as well as interface boundaries between neighboring elevation layers, as well as following a 1D curve and modifying the radial (normal to the extrusion axis) extent of the geometry.", "href": "../../source/meshgenerators/AdvancedExtruderGenerator.html"}>>>
    direction<<<{"description": "A vector that points in the direction to extrude (note, this will be normalized internally - so don't worry about it here)"}>>> = '0 0 1'
    heights<<<{"description": "The height of each elevation"}>>> = '4 4 4'
    num_layers<<<{"description": "The number of layers for each elevation - must be num_elevations in length!"}>>> = '1 1 1'
    bottom_boundary<<<{"description": "The boundary name to set on the bottom boundary. If omitted an ID will be generated."}>>> = 'bottom'
    top_boundary<<<{"description": "The boundary name to set on the top boundary. If omitted an ID will be generated."}>>> = 'top'
    input<<<{"description": "The mesh to extrude"}>>> = annular
  []
[]
(modules/porous_flow/examples/tutorial/00.i)

Then it is shifted downwards (ie, in the negative direction) by 6m to place the injection region around the origin:

[Mesh<<<{"href": "../../syntax/Mesh/index.html"}>>>]
  [shift_down]
    type = TransformGenerator<<<{"description": "Applies a linear transform to the entire mesh.", "href": "../../source/meshgenerators/TransformGenerator.html"}>>>
    transform<<<{"description": "The type of transformation to perform (TRANSLATE, TRANSLATE_CENTER_ORIGIN, TRANSLATE_MIN_ORIGIN, ROTATE, SCALE, ROTATE_WITH_MATRIX, ROTATE_EXT)"}>>> = TRANSLATE
    vector_value<<<{"description": "The value to use for the transformation. When using TRANSLATE or SCALE, the xyz coordinates are applied in each direction respectively. When using ROTATE, the values are interpreted as the Euler angles phi, theta and psi given in degrees. For ROTATE_EXT, an extrinsic rotation is carried out using prescribed Euler angles alpha, beta, and gamma in degrees."}>>> = '0 0 -6'
    input<<<{"description": "The mesh we want to modify"}>>> = make3D
  []
[]
(modules/porous_flow/examples/tutorial/00.i)

An aquifer region is created in the central 6m:

[Mesh<<<{"href": "../../syntax/Mesh/index.html"}>>>]
  [aquifer]
    type = SubdomainBoundingBoxGenerator<<<{"description": "Changes the subdomain ID of elements either (XOR) inside or outside the specified box to the specified ID.", "href": "../../source/meshgenerators/SubdomainBoundingBoxGenerator.html"}>>>
    block_id<<<{"description": "Subdomain id to set for inside/outside the bounding box"}>>> = 1
    bottom_left<<<{"description": "The bottom left point (in x,y,z with spaces in-between)."}>>> = '0 0 -2'
    top_right<<<{"description": "The bottom left point (in x,y,z with spaces in-between)."}>>> = '10 10 2'
    input<<<{"description": "The mesh we want to modify"}>>> = shift_down
  []
[]
(modules/porous_flow/examples/tutorial/00.i)

and an injection area is created on the borehole's wall in the aquifer region:

[Mesh<<<{"href": "../../syntax/Mesh/index.html"}>>>]
  [injection_area]
    type = ParsedGenerateSideset<<<{"description": "A MeshGenerator that adds element sides to a sideset if the centroid of the side satisfies the `combinatorial_geometry` expression.", "href": "../../source/meshgenerators/ParsedGenerateSideset.html"}>>>
    combinatorial_geometry<<<{"description": "Function expression encoding a combinatorial geometry"}>>> = 'x*x+y*y<1.01'
    included_subdomains<<<{"description": "A set of subdomain names or ids whose sides will be included in the new sidesets. A side is only added if the subdomain id of the corresponding element is in this set."}>>> = 1
    new_sideset_name<<<{"description": "The name of the new sideset"}>>> = 'injection_area'
    input<<<{"description": "The mesh we want to modify"}>>> = 'aquifer'
  []
[]
(modules/porous_flow/examples/tutorial/00.i)

Finally, the subdomains are named "caps" (for the upper and lower caprock) and "aquifer":

[Mesh<<<{"href": "../../syntax/Mesh/index.html"}>>>]
  [rename]
    type = RenameBlockGenerator<<<{"description": "Changes the block IDs and/or block names for a given set of blocks defined by either block ID or block name. The changes are independent of ordering. The merging of blocks is supported.", "href": "../../source/meshgenerators/RenameBlockGenerator.html"}>>>
    old_block<<<{"description": "Elements with these block ID(s)/name(s) will be given the new block information specified in 'new_block'"}>>> = '0 1'
    new_block<<<{"description": "The new block ID(s)/name(s) to be given by the elements defined in 'old_block'."}>>> = 'caps aquifer'
    input<<<{"description": "The mesh we want to modify"}>>> = 'injection_area'
  []
[]
(modules/porous_flow/examples/tutorial/00.i)

This process has created the 3D mesh. Each of the input files in the tutorial follow this process. The result is shown in Figure 1.

The 3D mesh.  The aquifer is shown in red and the caprocks in blue.  The green surface is the injection area.

Figure 1: The 3D mesh. The aquifer is shown in red and the caprocks in blue. The green surface is the injection area.

If this were a real simulation rather than just a tutorial, it would be much more efficient to use cylindrical coordinates, which are called "RZ" coordinates in MOOSE. The mesh-generation process is

[Mesh<<<{"href": "../../syntax/Mesh/index.html"}>>>]
  [gen]
    type = GeneratedMeshGenerator<<<{"description": "Create a line, square, or cube mesh with uniformly spaced or biased elements.", "href": "../../source/meshgenerators/GeneratedMeshGenerator.html"}>>>
    dim<<<{"description": "The dimension of the mesh to be generated"}>>> = 2
    nx<<<{"description": "Number of elements in the X direction"}>>> = 10
    xmin<<<{"description": "Lower X Coordinate of the generated mesh"}>>> = 1.0
    xmax<<<{"description": "Upper X Coordinate of the generated mesh"}>>> = 10
    bias_x<<<{"description": "The amount by which to grow (or shrink) the cells in the x-direction."}>>> = 1.4
    ny<<<{"description": "Number of elements in the Y direction"}>>> = 3
    ymin<<<{"description": "Lower Y Coordinate of the generated mesh"}>>> = -6
    ymax<<<{"description": "Upper Y Coordinate of the generated mesh"}>>> = 6
  []
  [aquifer]
    type = SubdomainBoundingBoxGenerator<<<{"description": "Changes the subdomain ID of elements either (XOR) inside or outside the specified box to the specified ID.", "href": "../../source/meshgenerators/SubdomainBoundingBoxGenerator.html"}>>>
    block_id<<<{"description": "Subdomain id to set for inside/outside the bounding box"}>>> = 1
    bottom_left<<<{"description": "The bottom left point (in x,y,z with spaces in-between)."}>>> = '0 -2 0'
    top_right<<<{"description": "The bottom left point (in x,y,z with spaces in-between)."}>>> = '10 2 0'
    input<<<{"description": "The mesh we want to modify"}>>> = gen
  []
  [injection_area]
    type = ParsedGenerateSideset<<<{"description": "A MeshGenerator that adds element sides to a sideset if the centroid of the side satisfies the `combinatorial_geometry` expression.", "href": "../../source/meshgenerators/ParsedGenerateSideset.html"}>>>
    combinatorial_geometry<<<{"description": "Function expression encoding a combinatorial geometry"}>>> = 'x<1.0001'
    included_subdomains<<<{"description": "A set of subdomain names or ids whose sides will be included in the new sidesets. A side is only added if the subdomain id of the corresponding element is in this set."}>>> = 1
    new_sideset_name<<<{"description": "The name of the new sideset"}>>> = 'injection_area'
    input<<<{"description": "The mesh we want to modify"}>>> = 'aquifer'
  []
  [rename]
    type = RenameBlockGenerator<<<{"description": "Changes the block IDs and/or block names for a given set of blocks defined by either block ID or block name. The changes are independent of ordering. The merging of blocks is supported.", "href": "../../source/meshgenerators/RenameBlockGenerator.html"}>>>
    old_block<<<{"description": "Elements with these block ID(s)/name(s) will be given the new block information specified in 'new_block'"}>>> = '0 1'
    new_block<<<{"description": "The new block ID(s)/name(s) to be given by the elements defined in 'old_block'."}>>> = 'caps aquifer'
    input<<<{"description": "The mesh we want to modify"}>>> = 'injection_area'
  []
[]
(modules/porous_flow/examples/tutorial/00_2D.i)