2 model,model_data,U,NU_ind)
4 % model,model_data[,U,NU_ind])
5 % computes convection contribution to finite volume time evolution matrices,
6 % <b> or their Frechet derivative </b>
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
12 % `L_I U^{k+1} = L_E U^k + b_E + b_I`.
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
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
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|}`.
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
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
41 % conv_flux_ptr :
function pointer specifying the
function `f`.
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
50 % L_E_conv : sparse matrix `L_{\text{lf}}` or `DL_{\text{lf}}|_{u}`
if flux
52 % bdir_E_conv : offset vector `b_{\text{lf}}`
55 % determine affine_decomposition_mode as integer
56 decomp_mode = model.decomp_mode;
59 if ~isempty(model_data)
60 grid = model_data.grid;
63 %%%%%%%%%%%%%%%%%%%%%%%%%%%%% lax-friedrichs %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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(:)];
73 disp(['found ',num2str(length(real_NB_ind)),' non-boundary edges.'])
74 disp(['found ',num2str(length(dir_NB_ind)),' dirichlet-boundary edges.'])
81 lambda = model.lxf_lambda;
83 % by using u=1, we get the velocity field
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;
90 model.conv_flux_ptr = old_conv_flux_ptr;
92 % disp('halt after flux_mat')
94 else % decomp_mode == 2
99 V.Vx = reshape(flux_mat(1,:,:),size(grid.ECX));
100 V.Vy = reshape(flux_mat(2,:,:),size(grid.ECX));
102 %%%%%%%% explicit matrix:
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)
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) * ...
120 L_E_diag = sparse(1:n,1:n, grid.Ainv(:) .* sum(vn+la,2));
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;
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);
134 error('error: lambda chosen too large! non stable LxF-scheme!');
138 L_E_conv = L_E_conv(NU_ind, :);
141 % disp('debug halt:')
144 % check row-sum of L_E_conv: must be positive!
145 % -> no, only for non-neuman-elements
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)
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) .* ...
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');
170 elseif decomp_mode==1
171 if ~model.flux_linear
172 error('flux needs to be linear')
174 % first component is lambda-term, remaining terms as
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);
183 la = zeros(size(grid.ESX));
184 la(real_or_dir_NB_ind) = 0.5 * grid.EL(real_or_dir_NB_ind) * ...
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;
193 % velocity components
195 V.Vx = reshape(flux_mat{q}(1,:,:), size(grid.ECX));
196 V.Vy = reshape(flux_mat{q}(2,:,:), size(grid.ECX));
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));
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;
209 else % decomp_mode == 2 -> coefficients
210 L_E_conv = [1; flux_mat(:)];
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))
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);
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);
229 bdir_E_conv = zeros(grid.nelements,1);
231 elseif decomp_mode == 1
232 if ~isempty(dir_NB_ind > 0)
233 %
for each u-component compute 1+Q_v components
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);
240 Q_Udir = length(Udir);
241 bdir_E_conv = cell(Q_Udir * (Q_v + 1),1);
243 val2 = zeros(size(grid.ESX));
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
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);
260 % still produce dummy components as the availability of dir
261 % boundary cannot be detected in online phase
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);
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);
277 bdir_E_conv((q1-1)*(Q_v +1)+1) = Udir(q1);
279 bdir_E_conv((q1-1)*(Q_v+1)+1+q2) = Udir(q1)*flux_mat(q2);
function r = verbose(level, message, messageId)
This function displays messages depending on a message-id and/or a level. Aditionally you can set/res...
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.