1 function num_flux = fv_num_conv_flux(model,model_data,U)
2 %
function num_flux = fv_num_conv_flux(model,model_data,U)
4 %
function computing a numerical convective flux
5 % matrix. Dirichlet-boundary treatment is performed, but Neuman-values
6 % are set to nan, must be performed by calling
function. Currently implemented
7 % lax-friedrichs and engquist-osher.
10 % Lg: lipschitz constant satisfying
11 % |g_ij(w,v) - g_ij(w
',v')| <= Lg |S_ij| ( |w
'-w| + |v'-v| )
12 % G: the matrix of numerical flux values across the edges.
13 % boundary treatment is performed as follows:
14 % in dirichlet-boundary points, the neighbour-values are simply set
15 % to
this value and the flux computed.
16 % Neuman-treatment is not performed here
18 % required fields of model:
19 % name_convective_num_flux :
'none',
'lax-friedrichs',
'engquist-osher'
21 % in
case of lax-friedrichs, either as diffusivity the value of the
22 % flux_matrix is used or
explicit setting of the value by
23 % model.lxf_lambda is possible.
25 %
for engquist-osher, we assume
for the convective flux
function:
26 % 1. f
'(u)*n does not change sign on interval(u,0)
28 % additionally, a conv_flux_derivative data-function must be available.
30 % plus additional fields required by dirichlet_values
32 % if grid is empty, it is generated
34 % Bernard Haasdonk 10.4.2006
36 grid = model_data.grid;
38 % if ~isfield(model,'flux_quad_degree
')
39 % model.flux_quad_degree = 1;
43 num_flux.G = nan* ones(size(grid.NBI));
45 % determine index sets of boundary types
46 real_NB_ind = find(grid.NBI>0);
47 NBIind = grid.NBI(real_NB_ind);
48 INBind = grid.INB(real_NB_ind);
49 % NB_real_ind comprises comprises some kind of inverse cell-neighbour
50 % relation that leads back to the original cell from where we started
52 % [i,j]=ind2sub(size(grid.NBI),NB_real_NB_ind(sub2ind(size(grid.NBI),k,l)))
55 NB_real_NB_ind = sub2ind(size(grid.NBI),NBIind,INBind);
57 dir_NB_ind = find(grid.NBI == -1);
59 % matrix with the neighbouring function values
60 % UNB = nan * ones(size(flux_mat.Fx));
61 UNB = nan * ones(size(grid.NBI));
62 UNB(real_NB_ind) = U(grid.NBI(real_NB_ind));
63 UU = repmat(U,1,size(grid.NBI,2));
65 %%%%%%%%%%%%%%%%%%% convective numerical flux: Lax-Friedrichs %%%%%%%%%%%%%%
66 if isequal(model.name_convective_num_flux,'nonlinear-diffusion
')
67 % g_jl(u,v) = |S_ij| K(grid.ESX,grid.ESY, u(ES))(psi(v)-psi(u))
68 % z.B. u(ES) = 0.5(u+v)
71 %%%%%%%%%%%%%%%%%%% convective numerical flux: Lax-Friedrichs %%%%%%%%%%%%%%
72 elseif isequal(model.name_convective_num_flux,'lax-friedrichs
')
73 % Lax-Friedrichs-Flux in 2D:
74 % g_jl(u,v) = 1/2 * nu_jl (f(u,Sx,Sy) + f(v,Sx,Sy)) -
75 % |S_jl|/(2 lambda_jl) (v-u)
77 % g_jl(u,v) = 1/2 |S_ij| * (n_jl * (f(u)+f(v)) - ...
78 % 1/(2 lambda_jl) (v-u))
79 % mit lambda_jl := dt / |m_j - m_l|
81 % FxNB and FyNB and UNB are computed, where possible:
82 % both are valid in real_NB_ind
84 % matrix evaluating the flux by quadratures over all edges
85 flux_mat = fv_conv_flux_matrix(model,model_data,U);
87 % in case of divergence cleaning, the velocity field must be
88 % available!! This is obtianed by setting u to 1
89 if (model.divclean_mode>0)
90 Udummy = ones(size(U));
91 flux_mat_Uone = fv_conv_flux_matrix(model,model_data,Udummy);
94 % matrix with flux values, the neighbour values U(j) inserted
95 % values must be reflected across all edges
96 % i.e. GNB(i,j) = flux.G(NBI(i,j),INB(i,j))
97 % take care of boundary-indices!!
98 FxNB = nan * ones(size(flux_mat.Fx));
99 % [i.j] = ind2sub(size(flux_mat.G),real_NB_ind);
100 FxNB(real_NB_ind) = flux_mat.Fx(NB_real_NB_ind);
102 FyNB = nan * ones(size(flux_mat.Fy));
103 % [i.j] = ind2sub(size(flux_mat.G),real_NB_ind);
104 FyNB(real_NB_ind) = flux_mat.Fy(NB_real_NB_ind);
106 % L1 is the supremum in space of the derivative of the first component
107 % L2 is the supremum in space of the derivative of the second component
108 L1 = max(max(abs(flux_mat.Fx)));
109 L2 = max(max(abs(flux_mat.Fy)));
111 % Lipschitz-constant of LxF Flux
112 num_flux.Lg = 1/(2*flux_mat.lambda) + 1/2 * norm([L1, L2]);
113 % an alternative formula without motivation:
114 % num_flux.Lg = max(max(grid.S)) * model.c * 0.25;
116 % in the linear case the following worked:
117 % numflux_mat.G = 1/2 * grid.S .* ...
118 % (grid.Nx.* (flux_mat.Vx.*(OU + OUext(grid.NBI))) + ...
119 % grid.Ny.* (flux_mat.Vy.*(OU + OUext(grid.NBI)))) ...
120 % + 1/(2 * flux_mat.lambda) * grid.S .* (OU - OUext(grid.NBI));
122 lambda = flux_mat.lambda;
123 if ~isempty(model.lxf_lambda)
124 lambda = model.lxf_lambda;
127 % in the general nonlinear case:
128 num_flux.G = 1/2 * grid.EL .* ...
129 (grid.NX.* (flux_mat.Fx + FxNB) + ...
130 grid.NY.* (flux_mat.Fy + FyNB)) ...
131 + 1/(2 * lambda) * grid.EL .* (UU - UNB);
133 % Dirichlet boundary treatment
134 if ~isempty(dir_NB_ind>0)
135 % determine dirichlet-boundary values as required by convective
136 % and diffusive flux. Neumann determined at end
137 Xdir = grid.ECX(dir_NB_ind);
138 Ydir = grid.ECY(dir_NB_ind);
139 Udir = model.dirichlet_values_ptr([Xdir,Ydir],model);
141 % if conv_flux_matrix has been divergence cleaned, no further
142 % flux evaluations may be performed!!
143 if ~isequal(model.divclean_mode,'none
')
145 disp('skipping boundary flux eval. due to divergence cleaning!!
');
147 % the following is unstable, if U reaches 0!!!!
148 % Fdir.Fx = flux_mat.Fx(dir_NB_ind)./UU(dir_NB_ind).*Udir;
149 % Fdir.Fy = flux_mat.Fy(dir_NB_ind)./UU(dir_NB_ind).*Udir;
150 % instead again evaluation of the whole matrix.
151 Fdir.Fx = flux_mat_Uone.Fx(dir_NB_ind).*Udir;
152 Fdir.Fy = flux_mat_Uone.Fy(dir_NB_ind).*Udir;
154 else % ordinary flux evaluation by quadratures
155 % Fdir = conv_flux(model,Udir, Xdir, Ydir);
156 [elids, edgeids] = ind2sub(size(grid.VI),dir_NB_ind);
157 PP = edge_quad_points(grid,elids, edgeids, ...
158 model.flux_quad_degree);
160 % the following is only relevant in case of use of a
161 % model.use_velocitymatrix_file and filecaching mode 2
162 if model.filecache_velocity_matrixfile_extract == 2;
163 model.velocity_matrixfile = ...
164 cache_velocity_matrixfile_extract(model,PP(1,:),PP(2,:),...
168 ff = conv_flux(model, repmat(Udir(:),model.flux_quad_degree,1), ...
170 Fdir.Fx = edge_quad_eval_mean(grid,elids,edgeids,...
171 model.flux_quad_degree,ff.Fx);
172 Fdir.Fy = edge_quad_eval_mean(grid,elids,edgeids,...
173 model.flux_quad_degree,ff.Fy);
176 % num_flux.G(dir_NB_ind) = grid.S(dir_NB_ind) .* ...
177 % (grid.Nx(dir_NB_ind) .* Fdir.Fx + ...
178 % grid.Ny(dir_NB_ind) .* Fdir.Fy);
180 % better solution: Lxf with "ghost-cells"
181 num_flux.G(dir_NB_ind) = 1/2 * grid.EL(dir_NB_ind) .* ...
182 (grid.NX(dir_NB_ind).* (flux_mat.Fx(dir_NB_ind) + Fdir.Fx) + ...
183 grid.NY(dir_NB_ind).* (flux_mat.Fy(dir_NB_ind) + Fdir.Fy)) ...
184 + 1/(2 * lambda) * grid.EL(dir_NB_ind) .* ...
185 (UU(dir_NB_ind) - Udir);
187 end; % of Dirichlet treatment
189 %%%%%%%%%%%%%%%%%%% convective numerical flux: Engquist Osher %%%%%%%%%%%%%%
190 elseif isequal(model.name_convective_num_flux,'engquist-osher
') ...
191 || isequal(model.name_convective_num_flux,'enquist-osher
')
193 % Engquist-Osher-Flux in 2D:
194 % g_jl(u,v) = |e_jl| * (c^+_jl(u) + c^-_jl(v))
196 % c^+_jl(u) = c_jl(0) + int_0^u max(c_jl'(s),0) ds
197 % c^-_jl(u) = int_0^u min(c_jl
'(s),0) ds
198 % c_jl(u) = f(u) * n_jl
201 % 1. f'(u)*n does not change sign on [0,u] and
205 % c^+_jl(u) = / f(u)*n_jl
if f
'(u)*n_jl > 0
207 % c^-_jl(v) = / f(v)*n_jl if f'(v)*n_jl < 0
211 if model.divclean_mode>0
212 error(
'divergence cleaning not implemented for engquist-osher!')
215 % evaluate flux matrix
216 % FxNB and FyNB and UNB are computed, where possible:
217 % both are valid in real_NB_ind
219 % matrix evaluating the flux by quadratures over all edges
222 % matrix with flux values, the neighbour values U(j) inserted
223 % values must be reflected across all edges
224 % i.e. GNB(i,j) = flux.G(NBI(i,j),INB(i,j))
225 % take care of boundary-indices!!
226 FxNB = nan * ones([grid.nelements,grid.nneigh]);
227 FyNB = nan * ones([grid.nelements,grid.nneigh]);
228 %[i,j] = ind2sub(size(flux_mat.G),NB_real_NB_ind);
229 [i,j] = ind2sub([grid.nelements, grid.nneigh],NB_real_NB_ind);
230 Fx = reshape(flux_mat(1,:,:),size(FxNB));
231 Fy = reshape(flux_mat(2,:,:),size(FxNB));
232 FxNB(real_NB_ind) = Fx(NB_real_NB_ind);
233 FyNB(real_NB_ind) = Fy(NB_real_NB_ind);
236 % evaluate flux matrix derivative (average over edges)
237 flux_derivative_mat = fv_conv_flux_linearization_matrix(model,model_data,U);
238 % produced fields: Vx, Vy
240 % matrix with flux values, the neighbour values U(j) inserted
241 % values must be reflected across all edges
242 % i.e. GNB(i,j) = flux.G(NBI(i,j),INB(i,j))
243 % take care of boundary-indices!!
244 VxNB = nan * ones(size(flux_derivative_mat.Vx));
245 % [i.j] = ind2sub(size(flux_mat.G),real_NB_ind);
246 VxNB(real_NB_ind) = flux_derivative_mat.Vx(NB_real_NB_ind);
248 VyNB = nan * ones(size(flux_derivative_mat.Vy));
249 % [i.j] = ind2sub(size(flux_mat.G),real_NB_ind);
250 VyNB(real_NB_ind) = flux_derivative_mat.Vy(NB_real_NB_ind);
252 if ~isempty(dir_NB_ind>0)
253 % ordinary flux evaluation by quadratures
254 % Fdir = conv_flux(model, Udir, Xdir, Ydir);
255 Xdir = grid.ECX(dir_NB_ind);
256 Ydir = grid.ECY(dir_NB_ind);
257 Udir = model.dirichlet_values_ptr([Xdir,Ydir],model);
258 [elids, edgeids] = ind2sub(size(grid.VI),dir_NB_ind);
259 PP = edge_quad_points(grid,elids, edgeids,model.flux_quad_degree);
261 ff = conv_flux(model, repmat(Udir(:),model.flux_quad_degree,1), ...
263 Fdir.Fx = edge_quad_eval_mean(grid,elids,edgeids,...
264 model.flux_quad_degree,ff.Fx);
265 Fdir.Fy = edge_quad_eval_mean(grid,elids,edgeids,...
266 model.flux_quad_degree,ff.Fy);
268 ff_lin = conv_flux_linearization(model, repmat(Udir(:),...
269 model.flux_quad_degree,...
272 Fdir_derivative.Vx = edge_quad_eval_mean(grid,elids,edgeids,...
273 model.flux_quad_degree,...
275 Fdir_derivative.Vy = edge_quad_eval_mean(grid,elids,edgeids,...
276 model.flux_quad_degree,...
279 FxNB(dir_NB_ind) = Fdir.Fx(:);
280 FyNB(dir_NB_ind) = Fdir.Fy(:);
281 VxNB(dir_NB_ind) = Fdir_derivative.Vx(:);
282 VyNB(dir_NB_ind) = Fdir_derivative.Vy(:);
286 % the following nicely maintains nan in Neumann-boundary edges
287 c_jl_u_deri = (grid.NX.* flux_derivative_mat.Vx + ...
288 grid.NY.* flux_derivative_mat.Vy);
289 I_c_jl_u_deri_pos = find (c_jl_u_deri > 0);
290 I_c_jl_u_deri_nonpos = c_jl_u_deri <= 0;
292 c_jl_v_deri = (grid.NX.* VxNB + grid.NY.* VyNB);
293 I_c_jl_v_deri_neg = find(c_jl_v_deri < 0);
294 I_c_jl_v_deri_nonneg = c_jl_v_deri >= 0;
296 c_jl_u_plus = nan * ones(size(grid.NBI)); % matrix of Nans
297 c_jl_u_plus(I_c_jl_u_deri_pos) = ...
298 grid.NX(I_c_jl_u_deri_pos).* Fx(I_c_jl_u_deri_pos) + ...
299 grid.NY(I_c_jl_u_deri_pos).* Fy(I_c_jl_u_deri_pos);
300 c_jl_u_plus(I_c_jl_u_deri_nonpos) = 0;
302 c_jl_v_minus = nan * ones(size(grid.NBI));
303 c_jl_v_minus(I_c_jl_v_deri_neg) = ...
304 grid.NX(I_c_jl_v_deri_neg).* FxNB(I_c_jl_v_deri_neg) + ...
305 grid.NY(I_c_jl_v_deri_neg).* FyNB(I_c_jl_v_deri_neg);
306 c_jl_v_minus(I_c_jl_v_deri_nonneg) = 0;
308 % add both components for final flux in all inner edges
309 num_flux.G = grid.EL.*(c_jl_u_plus + c_jl_v_minus);
311 % Lipschitz konstant Lg
312 % |g_ij(w,v) - g_ij(w',v')| <= Lg |S_ij| ( |w'-w| + |v'-v| )
314 num_flux.Lg = 1/ lambda;
316 %%%%%%%%%%%%%%%%%%% convective numerical flux: None %%%%%%%%%%%%%%
317 elseif isequal(model.name_convective_num_flux,'none')
321 error ('convective numerical flux unknown');
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...