rbmatlab  1.13.10
 All Classes Namespaces Files Functions Variables Groups Pages
fv_num_diff_flux_gradient_tensor.m
Go to the documentation of this file.
1 function num_flux = fv_num_diff_flux_gradient_tensor(model,model_data,U, NU_ind)
2 %function num_flux = fv_num_diff_flux_gradient_tensor(model,model_data,U, NU_ind)
3 % computes a numerical diffusive flux for a diffusion problem including a tensor
4 %
5 % This function computes a numerical diffusive flux matrix for a diffusion
6 % problem. Dirichlet boundary treatment is performed, but neuman values are set
7 % to 'NaN'. These must be handled by the calling function.
8 %
9 % For discretization of a 'gradient_tensor' the parameter NU_ind is
10 % necessary, if the gradient shall only be approximated on the local
11 % grid coordinates given by NU_ind.
12 %
13 % The analytical term inspiring this flux looks like
14 % `` - \nabla \cdot \left( d(x) K(x,u) \nabla u \right),``
15 % with a function `d: R \to R` and a tensor `K: R \times R \to \text{Lin}(R^2, R^2)`.
16 %
17 % The implemented flux then can be written for inner boundaries as
18 % ``g_{ij}(u,v) = d(x_{ij}) \tilde{K}(x,u,v)
19 % \tilde{\nabla}(u,v) |e_{ij}|``
20 % and for Dirichlet boundaries as
21 % ``g_{dir,ij}(u) = d(x_{ij}) \tilde{K}_{dir}(x,u) \tilde{\nabla}(u) |e_{ij}|``
22 % where the discretizations `\tilde{K} and \tilde{\nabla}` of `K`
23 % respectively `\nabla` are given by the function pointer
24 % 'model.diffusivity_tensor_ptr' respectively the function gradient_approx().
25 % Note, that the latter is only available for rectangular grids.
26 %
27 % required fields of model:
28 % diffusivity_ptr : function pointer to diffusivity function `d`
29 % diffusivity_tensor_ptr : function pointer to a tensor implementation for `K`
30 %
31 % generated fields of num_flux:
32 % epsilon: diffusion coefficient bound
33 % G: the matrix of numerical flux values across the edges.
34 % boundary treatment is performed as follows:
35 % in dirichlet-boundary edges, the neighbour-values are simply set
36 % to this value and the flux is computed.
37 %
38 % if grid is empty, it is generated
39 
40 % Bernard Haasdonk 10.4.2006
41 
42 grid = model_data.grid;
43 
44 if isempty(NU_ind)
45  NU_ind = 1:grid.nelements;
46 end
47 
48 %nu_ind_length = length(NU_ind);
49 
50 num_flux = [];
51 num_flux.epsilon = 0;
52 num_flux.G = zeros(size(grid.NBI));
53 
54 % determine index sets of boundary types
55 real_NB_ind = find(grid.NBI>0);
56 dir_NB_ind =find(grid.NBI == -1);
57 neu_NB_ind =find(grid.NBI == -2);
58 
59 if model.verbose >= 29
60  disp(['found ',num2str(length(real_NB_ind)),' non-boundary edges.'])
61  disp(['found ',num2str(length(dir_NB_ind)),' dirichlet-boundary edges.'])
62  disp(['found ',num2str(length(neu_NB_ind)),' neumann-boundary edges.'])
63 end
64 
65 % get values in centers of the cells adjacent to the edge an the edges
66 % corner points. The corner values are computed by a 'weighted' average
67 % over the four adjacent cells. Then do an lsq approximation of the
68 % face the four points lie on. QUESTION: How can this be done in 3d and
69 % on general grids?
70 if model.debug && ~isequal(model.gridtype, 'rectgrid')
71  error('gradient_tensor only implemented for rectangular grids');
72 end
73 
74 G_with_nans = zeros(size(num_flux.G));
75 
76 for edge = 1:4
77  diff = model.diffusivity_tensor_ptr([grid.ECX(NU_ind,edge), ...
78  grid.ECY(NU_ind,edge)], ...
79  U, model, 1);
80  model.U = U(NU_ind);
81  diff_k = model.diffusivity_ptr([grid.ECX(NU_ind,edge),...
82  grid.ECY(NU_ind,edge)], [], model);
83 
84  % construct the diffusivity matrix D for current edge
85  %[tmpgrad,bdir] = gradient_approx_matrix(model,model_data,...
86  % NU_ind,edge);
87  [tmpgrad] = gradient_approx(model,model_data,U,NU_ind,edge);
88 
89  % tmp2 stores the matrix vector product of 'diffusion tensor x
90  % gradient'
91  vlen = size(tmpgrad,1);
92  tmpgrad = reshape(tmpgrad',2*vlen,1);
93 
94  % compute on each intersection e = e_{i,edge} the diffusion tensor
95  % D_e = d_e * [ T_{e11}, T_{e12};
96  % T_{e21}, T_{e22} ] ...
97  tmp1 = spdiags(repmat(diff_k.K,2,1),0,size(diff.K,1),size(diff.K,2)) * diff.K;
98  % ... and multiply it with approximated gradient over the intersection
99  % g_e \circ \nabla U | _e:
100  % h_e = D_e * g_e
101  % The result is
102  % tmp2 = [ h_{e_{1,edge}}; h_{e_{2,edge}}; ... ; h_{e_{H,edge}} ].
103  tmp2 = tmp1 * tmpgrad;
104 
105 % if edge == 2 || edge == 4
106 % quiver(grid.ECX(:,edge),grid.ECY(:,edge), tmpgrad(:,1), tmpgrad(:,2));
107 % hold on;
108 % quiver(grid.ECX(:,edge),grid.ECY(:,edge), tmp2(:,1), tmp2(:,2));
109 % hold off;
110 % keyboard;
111 % end
112 
113  tmp2 = reshape(tmp2, 2, vlen)';
114 
115  % M = length(NU_ind);
116  % offset = kron(M,NU_ind)-M;
117  % indices = repmat(offset',M,1)+repmat(NU_ind,1,M);
118 
119  G_with_nans(NU_ind,edge) = sum(tmp2 .* [ grid.NX(NU_ind,edge),...
120  grid.NY(NU_ind,edge) ], 2);
121 end
122 G_with_nans = - G_with_nans .* grid.EL;
123 num_flux.G(real_NB_ind) = G_with_nans(real_NB_ind);
124 num_flux.G(dir_NB_ind) = G_with_nans(dir_NB_ind);
125 
126 num_flux.epsilon = max(max(num_flux.G));
127