Grid Construction and Basic Interactions

Expected Read Time: 3~5 minutes

To implement the adaptive finite volume methods, we need to keep:

  • Connectivity/Neighbor structure of the cells
  • Edge between cells

consistent and define the drift and diffusion terms for the edges. afv_grid class is written to help working with this computation. To start, we initialize and set the dimensionality of the problem by

>> grid = afv_grid(2);

At this state, the grid is initialized with one cell of [0, 1] x [0, 1]. We can see this by checking the number of nodes via

>> disp(grid.num_n);

    1

and checking the boundaries of the one nodes

>> disp(grid.n2bd);

    0   0   1   1

The boundaries are ordered by lower boundaries and then upper boundaries (by coordinate dimension). Hence, the output is

[lower boundary dim 1, lower boundary dim 2, upper boundary dim 1, upper boundary dim 2]

Now, one can split the node by feeding in cut points, and using split_init

>> x_knots{1} = 1/2;
>> x_knots{2} = [1/3; 2/3];

>> grid.split_init(1, x_knots);

As we can see, we define cell array of knot points for each dimension. With 1 cut point in dimension 1 and 2 cut points in dimension 2, we end up with total of 6 cells. We can check this by checking num_n again

>> disp(grid.num_n);

    6

Checking boundaries of the nodes, we can see that nodes are split correctly

>> disp(grid.n2bd);

         0         0    0.5000    0.3333
    0.5000         0    1.0000    0.3333
         0    0.3333    0.5000    0.6667
    0.5000    0.3333    1.0000    0.6667
         0    0.6667    0.5000    1.0000
    0.5000    0.6667    1.0000    1.0000

A part of the requirement in computing the finite-volume discretization is keeping the “neighbor” structure of the grid so that one can compute the flows between cells. Keeping this structure together is the most difficult part of the implementation, and this structure is updated within the class as long as only supported functions (split_init, split, and add_new_nodes) are called. We can see the connectivity of the cells are updated properly by checking n2n

>> disp(grid.n2n);

(:,:,1) =

     []     []
    [1]     []
     []    [1]
    [3]    [2]
     []    [3]
    [5]    [4]


(:,:,2) =

    [2]    [3]
     []    [4]
    [4]    [5]
     []    [6]
    [6]     []
     []     []

where things correspond to (point, coordinate direction, forward=2/backward=1). For example, [2] in the (1,1,2) denotes that the neighbor forward of point 1 in the first coordinate direction is point 2. This neighbor information in n2n is used to build the transition matrix. Because these steps are automated, one never needs to (and probably should not) work with low-level representation like n2n directly.[#]_

We can further split the first grid by calling

>> x_knots{1} = 1/4;
>> x_knots{2} = 1/6;

>> grid.split(1, x_knots);

This is the process of adaptively updating grid points. Now, to build the the transition matrix of the corresponding finite-volume method, we need the information on the edges/interfaces between nodes. The edges will be built using n2n that has been kept consistent underneath the problem. One can setup the edges by calling extract_edges

>> grid.extract_edges();

This function call builds all internal edge related quantities. For example, we can see that there are 13 internal edges

>> disp(grid.num_e);

    13

Following similar syntax, we can check

  • boundaries: e2bd
  • connectivity to nodes: e2n
  • direction of the edge: e2dir

Again, for most applications, one would not need to work with low-level grid representation like e2n or e2dir, but instead work with the center of edges by calling

>> grid.compute_edge_midpoints()

ans =

    0.2500    0.0833
    0.5000    0.5000
    0.5000    0.8333
    0.5000    0.0833
    0.2500    0.2500
    0.5000    0.2500
    0.1250    0.1667
    0.7500    0.3333
    0.2500    0.6667
    0.7500    0.6667
    0.3750    0.1667
    0.1250    0.3333
    0.3750    0.3333

In practice, these are the points where one would compute the drift and diffusion for the Fokker-Planck equations. In this tutorial, a detailed description of the behaviors of the grid is given, but in most applications, one does not have to consider the underlying behaviors of the grid. One can just follow a recipe to use the finite volume method as will be shown in the next section.