rbmatlab  1.16.09
 All Classes Namespaces Files Functions Variables Modules Pages
fv_operators_conv_explicit_lax_friedrichs.m
Go to the documentation of this file.
1 function [L_E_conv,bdir_E_conv] = fv_operators_conv_explicit_lax_friedrichs(...
2  model,model_data,U,NU_ind)
3 %function [L_E_conv,bdir_E_conv] = fv_operators_conv_explicit_lax_friedrichs(...
4 % model,model_data[,U,NU_ind])
5 % computes convection contribution to finite volume time evolution matrices,
6 % <b> or their Frechet derivative </b>
7 %
8 % This function computes a convection operator `L_{\text{lf}}` and a
9 % corresponding offset vector `b_{\text{lf}}` that can be used by
10 % fv_operators_implicit_explicit() to build evolution matrices for a finite
11 % volume time step
12 % `L_I U^{k+1} = L_E U^k + b_E + b_I`.
13 %
14 % The analytical term inspiring this operator looks like
15 % `` v \cdot \nabla u ``
16 % or in the non-linear case, where the Frechet derivative is computed
17 % `` \nabla f(u). ``
18 % Here, `v` is a space dependent velocity field and `f` some smooth function
19 % in `C^2(\mathbb{R}^d, \mathbb{R})`. The fluxes can be controlled with the
20 % 'model.conv_flux_ptr' fields. The first case is default usage of this
21 % operator with the field 'params.conv_flux_ptr' pointing to
22 % conv_flux_linear() where the velocity field `v` is given through
23 % 'velocity_ptr'.
24 %
25 % The Lax-Friedrichs flux is used with fluxes:
26 % ``g_{jl}(u,v) = \frac12 (f(u,x_{jl}) + f(v,x_{jl})) \cdot \nu_{jl} -
27 % \frac{|S_{jl}|}{2 \lambda_{jl}} (v-u)``
28 % with `\lambda_{jl} := \frac{\Delta t}{|m_j - m_l|}`.
29 %
31 %
32 % Required fields of model:
33 % lxf_lambda : scalar `\lambda = \sup \lambda_{jl}` controlling the
34 % artificial diffusion added to the flux.
35 % dirichlet_values_ptr : function pointer for dirichlet function
36 % `u_{\text{dir}}`
37 % flux_linear : flag indicating wether the flux function `f` is linear. If
38 % this flag is set to 'false' and the decomp mode is set to
39 % 'complete' the Frechet derivative `DL_{\text{lf}}|_{u}` is
40 % returned.
41 % conv_flux_ptr : function pointer specifying the function `f`.
42 %
43 % Optional fields of model:
44 % conv_flux_derivative_ptr : function pointer specifying the derivative of
45 % the convection function `f`. This is only needed
46 % in case we want to compute the Frechet
47 % derivative.
48 %
49 % Return values:
50 % L_E_conv : sparse matrix `L_{\text{lf}}` or `DL_{\text{lf}}|_{u}` if flux
51 % is non-linear.
52 % bdir_E_conv : offset vector `b_{\text{lf}}`
53 %
54 
55 % determine affine_decomposition_mode as integer
56 decomp_mode = model.decomp_mode;
57 
58 grid = [];
59 if ~isempty(model_data)
60  grid = model_data.grid;
61 end
62 
63 %%%%%%%%%%%%%%%%%%%%%%%%%%%%% lax-friedrichs %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64 if decomp_mode < 2
65  n = grid.nelements;
66  % determine index sets of boundary types
67  real_NB_ind = find(grid.NBI>0);
68  %NBIind = grid.NBI(real_NB_ind);
69  %INBind = grid.INB(real_NB_ind);
70  dir_NB_ind =find(grid.NBI == -1);
71  real_or_dir_NB_ind = [real_NB_ind(:); dir_NB_ind(:)];
72  if model.verbose >= 29
73  disp(['found ',num2str(length(real_NB_ind)),' non-boundary edges.'])
74  disp(['found ',num2str(length(dir_NB_ind)),' dirichlet-boundary edges.'])
75  end;
76 
77  if nargin < 4
78  NU_ind = [];
79  end
80 
81  lambda = model.lxf_lambda;
82  if model.flux_linear
83  % by using u=1, we get the velocity field
84  flux_mat = fv_conv_flux_matrix(model,model_data,ones(n,1));
85  else
86  % non-linear case: use f' for flux matrix generation
87  old_conv_flux_ptr = model.conv_flux_ptr;
88  model.conv_flux_ptr = model.conv_flux_derivative_ptr;
89  flux_mat = fv_conv_flux_matrix(model, model_data, U);
90  model.conv_flux_ptr = old_conv_flux_ptr;
91  end
92  % disp('halt after flux_mat')
93  % keyboard;
94 else % decomp_mode == 2
95  flux_mat = fv_conv_flux_matrix(model,[],[]);
96 end;
97 
98 if decomp_mode == 0
99  V.Vx = reshape(flux_mat(1,:,:),size(grid.ECX));
100  V.Vy = reshape(flux_mat(2,:,:),size(grid.ECX));
101 
102  %%%%%%%% explicit matrix:
103  % has entries
104  % (L_E_conv)il = 1/|Ti| *
105  % (sum j (NB(i) cup NB_dir(i))
106  % 1/2 |S_ij| (v(c_ij)*n_ij + 1/lambda)) for l=i
107  % 1/|Ti| * 1/2 |S_il| (v(c_il)*n_il - 1/lambda) for l NB(i)
108  % 0 else
109 
110  % compute products
111  vn = zeros(size(grid.ESX));
112  vn(real_or_dir_NB_ind) = 0.5 * grid.EL(real_or_dir_NB_ind) .* ...
113  (V.Vx(real_or_dir_NB_ind).*grid.NX(real_or_dir_NB_ind)+ ...
114  V.Vy(real_or_dir_NB_ind).*grid.NY(real_or_dir_NB_ind));
115  la = zeros(size(grid.ESX));
116  la(real_or_dir_NB_ind) = 0.5 * grid.EL(real_or_dir_NB_ind) * ...
117  ( 1 / lambda);
118 
119  % diagonal entries
120  L_E_diag = sparse(1:n,1:n, grid.Ainv(:) .* sum(vn+la,2));
121 
122  % off-diagonal entries (nonpositive):
123  [i,dummy ]= ind2sub(size(grid.ESX),real_NB_ind);
124  L_E_offdiag = sparse(i,grid.NBI(real_NB_ind),grid.Ainv(i) .* ...
125  (vn(real_NB_ind) - la(real_NB_ind)), n,n);
126  L_E_conv = L_E_diag + L_E_offdiag;
127 
128  % check for stability: off-diagonal entries must be nonpositive
129  % (mult with -dt gives the coefficients Theta_ij in the convex combination
130  % u_i^(k+1) = (1-sum Theta_ij)u_i^k + sum Theta_ij u_j^k)
131  [i,j] = find(L_E_offdiag>0);
132  if model.verbose>=9
133  if ~isempty(i)
134  error('error: lambda chosen too large! non stable LxF-scheme!');
135  end;
136  end;
137  if ~isempty(NU_ind)
138  L_E_conv = L_E_conv(NU_ind, :);
139  end
140 
141  % disp('debug halt:')
142  % keyboard;
143 
144  % check row-sum of L_E_conv: must be positive!
145  % -> no, only for non-neuman-elements
146  %if model.verbose>=10
147  % if ~isempty(find(sum(L_E_conv,2)<0))
148  % disp('row sum of explicit matrix is smaller than zero!!')
149  % % derivation yields:
150  % % sum(L_E_conv,2)_i = T1(i) + T2(i)
151  % % with
152  % % T1(i) = 1/|Ti| sum_ (j NB(i) or NB_dir(i)) |Sij|(v_ij n_ij)
153  % % T2(i) = 1/|Ti| sum_ (j NB_dir(i)) 0.5* |Sij|(1/lambda - v_ij n_ij)
154  % m1 = zeros(size(grid.Sx));
155  % m2 = zeros(size(grid.Sx));
156  % m1(real_or_dir_NB_ind) = grid.S(real_or_dir_NB_ind).* ...
157  % (V.Vx(real_or_dir_NB_ind).*grid.Nx(real_or_dir_NB_ind)+ ...
158  % V.Vy(real_or_dir_NB_ind).*grid.Ny(real_or_dir_NB_ind));
159  % m2(dir_NB_ind) = 0.5 * grid.S(dir_NB_ind) .* ...
160  % (1/lambda - ...
161  % V.Vx(dir_NB_ind).*grid.Nx(dir_NB_ind)- ...
162  % V.Vy(dir_NB_ind).*grid.Ny(dir_NB_ind));
163  % T1 = sum(m1,2).*grid.Ainv(:);
164  % T2 = sum(m2,2).*grid.Ainv(:);
165  % disp('check T1+T2 = sum(L_E_conv,2) and nonnegativity of T1, T2');
166  % keyboard;
167  % end;
168  % end;
169 
170 elseif decomp_mode==1
171  if ~model.flux_linear
172  error('flux needs to be linear')
173  end
174  % first component is lambda-term, remaining terms as
175  % v-decomposition
176 
177  Q_v = length(flux_mat);
178  L_E_conv = cell(1+Q_v,1);
179  % auxiliary quantities required some times
180  [i,dummy ]= ind2sub(size(grid.ESX),real_NB_ind);
181 
182  % lambda-component
183  la = zeros(size(grid.ESX));
184  la(real_or_dir_NB_ind) = 0.5 * grid.EL(real_or_dir_NB_ind) * ...
185  ( 1 / lambda);
186  % diagonal entries
187  L_E_diag = sparse(1:n,1:n, grid.Ainv(:) .* sum(la,2));
188  % off-diagonal entries (nonpositive):
189  L_E_offdiag = sparse(i,grid.NBI(real_NB_ind),grid.Ainv(i) .* ...
190  ( - la(real_NB_ind)), n,n);
191  L_E_conv{1} = L_E_diag + L_E_offdiag;
192 
193  % velocity components
194  for q = 1:Q_v;
195  V.Vx = reshape(flux_mat{q}(1,:,:), size(grid.ECX));
196  V.Vy = reshape(flux_mat{q}(2,:,:), size(grid.ECX));
197  % compute products
198  vn = zeros(size(grid.ESX));
199  vn(real_or_dir_NB_ind) = 0.5 * grid.EL(real_or_dir_NB_ind) .* ...
200  (V.Vx(real_or_dir_NB_ind).*grid.NX(real_or_dir_NB_ind)+ ...
201  V.Vy(real_or_dir_NB_ind).*grid.NY(real_or_dir_NB_ind));
202  % diagonal entries
203  L_E_diag = sparse(1:n,1:n, grid.Ainv(:) .* sum(vn,2));
204  % off-diagonal entries (nonpositive):
205  L_E_offdiag = sparse(i,grid.NBI(real_NB_ind),grid.Ainv(i) .* ...
206  (vn(real_NB_ind)), n,n);
207  L_E_conv{q+1} = L_E_diag + L_E_offdiag;
208  end;
209 else % decomp_mode == 2 -> coefficients
210  L_E_conv = [1; flux_mat(:)];
211 end;
212 
213 %%%%%%%% dirichlet-offset-vector:
214 % (bdir_E_conv)_i = - 1/|T_i| * ...
215 % sum_j Ndir(i) (1/2 |S_ij| (v(c_ij)*n_ij-1/lambda) u_dir(c_ij,t))
216 if decomp_mode == 0
217  if ~isempty(dir_NB_ind > 0)
218  % evaluate dirichlet values at edge midpoints at t
219  Xdir = grid.ECX(dir_NB_ind);
220  Ydir = grid.ECY(dir_NB_ind);
221  Udir = model.dirichlet_values_ptr([Xdir,Ydir],model);
222 
223  val2 = zeros(size(grid.ESX));
224  val2(dir_NB_ind)= 0.5 * grid.EL(dir_NB_ind).* ...
225  (V.Vx(dir_NB_ind).*grid.NX(dir_NB_ind) + ...
226  V.Vy(dir_NB_ind).*grid.NY(dir_NB_ind)-1/lambda) .*Udir;
227  bdir_E_conv = - grid.Ainv(:).* sum(val2,2);
228  else
229  bdir_E_conv = zeros(grid.nelements,1);
230  end;
231 elseif decomp_mode == 1
232  if ~isempty(dir_NB_ind > 0)
233  % for each u-component compute 1+Q_v components
234 
235  % evaluate dirichlet values at edge midpoints at t
236  Xdir = grid.ECX(dir_NB_ind);
237  Ydir = grid.ECY(dir_NB_ind);
238  Udir = model.dirichlet_values_ptr([Xdir, Ydir], model);
239 
240  Q_Udir = length(Udir);
241  bdir_E_conv = cell(Q_Udir * (Q_v + 1),1);
242 
243  val2 = zeros(size(grid.ESX));
244  for q1 = 1:Q_Udir
245  % first component is lambda
246  val2(dir_NB_ind)= 0.5 * grid.EL(dir_NB_ind) * ...
247  (-1/lambda) .*Udir{q1};
248  bdir_E_conv{(q1-1)*(Q_v +1)+1} = - grid.Ainv(:).* sum(val2,2);
249  % remaining are velocity-components
250  for q2 = 1:Q_v
251  F.Fx = reshape(flux_mat{q2}(1,:,:),size(grid.ECX));
252  F.Fy = reshape(flux_mat{q2}(2,:,:),size(grid.ECX));
253  val2(dir_NB_ind)= 0.5 * grid.EL(dir_NB_ind).* ...
254  (F.Fx(dir_NB_ind).*grid.NX(dir_NB_ind) + ...
255  F.Fy(dir_NB_ind).*grid.NY(dir_NB_ind)) .*Udir{q1};
256  bdir_E_conv{(q1-1)*(Q_v+1)+1+q2} = - grid.Ainv(:).* sum(val2,2);
257  end;
258  end;
259  else
260  % still produce dummy components as the availability of dir
261  % boundary cannot be detected in online phase
262  tmodel = model;
263  tmodel.decomp_mode = 2;
264  Udir = model.dirichlet_values_ptr([],tmodel);
265  % for each combination of Udir and diffusivity component
266  % perform identical computation as above
267  Q_Udir = length(Udir);
268  bdir_E_conv = cell(Q_Udir * (Q_v+1),1);
269  end;
270 
271 else % decomp_mode == 2 -> coefficients
272  Q_v = length(flux_mat);
273  Udir = model.dirichlet_values_ptr([],model);
274  Q_Udir = length(Udir);
275  bdir_E_conv = zeros(Q_Udir * (Q_v + 1),1);
276  for q1 = 1:Q_Udir
277  bdir_E_conv((q1-1)*(Q_v +1)+1) = Udir(q1);
278  for q2 = 1:Q_v
279  bdir_E_conv((q1-1)*(Q_v+1)+1+q2) = Udir(q1)*flux_mat(q2);
280  end;
281  end;
282 end;
283 
284 
function r = verbose(level, message, messageId)
This function displays messages depending on a message-id and/or a level. Aditionally you can set/res...
Definition: verbose.m:17
function [ flux_mat , lambda ] = fv_conv_flux_matrix(model, model_data, U, conv_flux_ptr)
function computing the flux matrix of a convection problem. simply reformatting the grid data suitabl...
function num_flux = fv_num_conv_flux_lax_friedrichs(model, model_data, U, NU_ind)
Function computing a numerical convective Lax-Friedrichs flux matrix.
function [ L_E_conv , bdir_E_conv ] = fv_operators_conv_explicit_lax_friedrichs(model, model_data, U, NU_ind)
computes convection contribution to finite volume time evolution matrices, or their Frechet derivati...
function [ flux , lambda ] = conv_flux_linear(glob, U, params)
function computing the convective flux of a convection problem.