1 function U = fem_laplace(grid,params)
2 %
function U = fem_laplace(grid,params)
4 %
function solving the elliptic laplace problem with general dirichlet and
5 % neuman boundary data
using a nodal basis. currently cartesian
6 % grid (
rectgrid) is assumed and bilinear lagrange-elements. More general
7 % ansatz-functions and quadratures should be implemented in the future.
9 % Current problem: discrete gradient of the solution is not divergence
10 % free so must be divergence-cleaned if used as velocity field
11 % in other simulations
13 % Bernard Haasdonk 13.4.2006:
16 % grid=grid_geometry(params);
20 error('currently only
rectgrid supported for fem!!');
23 % indices of points on dirichlet boundary
24 dir_vertex = zeros(size(grid.X));
27 disp('entering fe_laplace');
30 % search all dirichlet edges and mark both corresponding vertices
32 disp('searching dirichlet edges');
34 dir_edge = grid.NBI == -1;
35 nfaces = size(grid.NBI,2);
37 di = find(dir_edge(:,i));
38 dir_vertex(grid.VI(di,i)) = 1;
39 dir_vertex(grid.VI(di,mod(i,nfaces)+1)) = 1;
41 dir_vind = find(dir_vertex);
43 ndof = length(grid.X);
44 dirichlet_fct = zeros(ndof,1);
46 params.decomp_mode = 0;
47 % determine dirichlet boundary values
51 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
52 % setup stiffness matrix as a sparse matrix
53 % S(i,j) = 1 if i==j and x_i is a dirichlet-vertex
54 % S(i,j) = 0 if i!=j and x_i is a dirichlet-vertex
55 % S(i,j) =
int sigma * grad phi_i * grad_phi_j if x_i,j are non-dirichlet
57 % so first compute complete stiffness-matrix
58 % (then kronecker-kill later on before system solving)
60 disp('setting up stiffness matrix');
62 %S = sparse([0]zeros(ndof));
63 S = spalloc(ndof,ndof,13*ndof);
65 % in the general case: compute all local matrices simultaneously
66 % L(i,j,k) contribution of element i, local basis functions j and k
68 % in our case, L is not element-dependent, so only compute L(j,k)
70 % for cartesian grid and constant sigma the local matrix is
71 % identical for all elements
72 % domain: [0, dx] x [0, dy],
73 % counting right lower corner 1 then counterclockwise
74 % detAinv = (dx*dy)^-1
75 % grad phi_1 = detAinv * [dy-y, -x ]'
76 % grad phi_2 = detAinv * [ y, x ]'
77 % grad phi_3 = detAinv * [ -y, dx-x]'
78 % grad phi_4 = detAinv * [y-dy, x-dx]'
79 % => L(j,k) = detAinv * sigma
80 % [1/3dx^2+1/3dy^2) -1/3dx^2+1/6dy^2 -1/6dx^2-1/6dy^2 1/6dx^2-1/3dy^2
81 % XXXX 1/3dx^2+1/3dy^2 1/6dx^2-1/3dy^2 -1/6dx^2-1/6dy^2
82 % XXXX XXXX 1/3dx^2+1/3dy^2 -1/3dx^2+1/6dy^2
83 % xxxx XXXX XXXX 1/3(dx^2+dy^2) ]
85 nbasisfcts = size(grid.VI,2);
87 dx = (params.xrange(2)-params.xrange(1))/params.xnumintervals;
88 dy = (params.yrange(2)-params.yrange(1))/params.ynumintervals;
90 Lx = [1/3 -1/3 -1/6 1/6;
95 Ly = [1/3 1/6 -1/6 -1/3;
100 L = (dx*dy)^(-1) * sigma * (Lx * dx^2 + Ly * dy^2);
102 % accumulate local matrices to single operator matrix
103 % avoid large element loop, instead perform loop over pairs of
104 % local basis functions
109 disp(['(j,k)=(',num2str(j),',',num2str(k),')']);
111 global_dofs_j = grid.VI(:,j);
112 global_dofs_k = grid.VI(:,k);
113 global_dofs_ind = sub2ind(size(S),global_dofs_j,global_dofs_k);
114 if max(global_dofs_ind)>1.6e9
115 % the following code results in t = 707.5058 s for 300x150 grid
116 % cut S into column-block-pieces for ommiting large-index accesses
117 % v = lget(S,global_dofs_ind);
119 % S = lset(S,global_dofs_ind,v);
120 % the following code results in t = 476.4325 s for 300x150 grid
121 % for z = 1:length(global_dofs_ind)
122 % S(global_dofs_j(z),global_dofs_k(z)) = ...
123 % S(global_dofs_j(z),global_dofs_k(z)) + L(j,k);
125 % the following code results in t = 62s s for 300x150 grid :-)
126 % most time spent in solution of LGS
127 V = sparse(global_dofs_j, global_dofs_k, ...
128 L(j,k),size(S,1),size(S,2));
130 else % direct subvector operation is possible
131 S(global_dofs_ind) = S(global_dofs_ind) + L(j,k);
132 % S(global_dofs_ind) = S(global_dofs_ind) + L(:,j,k);
137 % alternative: Loop over elements, (which is to be omitted)
138 % nelements = length(grid.A);
139 % for i = 1:nelements
140 % global_dofs_i = ...
141 % S(global_dofs_i,global_dofs_i) = ...
142 % S(global_dofs_i,global_dofs_i) + L(i,:,:);
145 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
146 % setup right hand side
147 % b(j) = T1 + T2 + T3 if x_j is a non-dirichlet vertex
148 % b(j) = 0 if x_j is a dirichlet vertex
150 % so first compute all nontrivial quantities, then set dir_vind to 0
152 %%%%%%%%% source term:
153 % T1 =
int source * phi_j (2D integral)
154 % -> zero in our case as we have no source
156 disp('setting up right hand side');
161 %%%%%%%%% dirichlet term:
162 % T2 = -
int sigma * grad b_dir * grad phi_j (negative of a 2D integral)
163 % after projection of b_dir to the discrete function space
164 % this is exactly the stiffness-matrix multiplied with the b_dir
166 T2 = - S * dirichlet_fct;
168 %%%%%%%%% neuman term:
169 % T3 = int_gamma_neu b_neu * phi_j (1D integral)
171 % goal: Assume b_neu also piecewise linear -> exact integration possible
172 % for all edges, the entries of the two corresponding nodes are modified
175 % search all neuman edges and generate list of start and end points
176 neu_edge = find(grid.NBI == -2);
177 [i,j] = ind2sub(size(grid.NBI),neu_edge);
178 % list of global indices of start points
179 neu_vind1 = grid.VI(neu_edge);
180 % list of global indices of end points
181 j2 = mod(j,nfaces)+1;
182 neu_edge_plus1 = sub2ind(size(grid.NBI),i,j2);
183 neu_vind2 = grid.VI(neu_edge_plus1);
185 neu_vals1= neuman_values(params, grid.X(neu_vind1),...
186 grid.Y(neu_vind1),[],[],[]);
187 neu_vals2= neuman_values(params, grid.X(neu_vind2),...
188 grid.Y(neu_vind2),[],[],[]);
189 % generate lists of neuman integral values
190 % neu_int1(neu_edgenr) is the integral contribution to start node
191 neu_lengths = grid.EL(neu_edge);
193 % determine elementary integrals
194 % (phi_1 is basis function of start point, phi_2 of end point)
195 % int_edge Phi_1 Phi_1 = 1/3 |S|
196 % int_edge Phi_1 Phi_2 = 1/6 |S|
197 % int_edge Phi_2 Phi_2 = 1/3 |S|
199 % neu_int1 = int_edge Phi_1 (neu_val1 * Phi1 + neu_val2 * Phi2)
200 neu_int1 = neu_lengths.* (1/3 * neu_vals1 + 1/6 * neu_vals2);
201 neu_int2 = neu_lengths.* (1/6 * neu_vals1 + 1/3 * neu_vals2);
202 T3(neu_vind1) = T3(neu_vind1) + neu_int1;
203 T3(neu_vind2) = T3(neu_vind2) + neu_int2;
209 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
210 %delete dirichlet-nodes in matrix
213 S(dir_vind,dir_vind) = speye(length(dir_vind));
215 % solve homogeneous problem
218 disp('solving LGS system');
221 %[U, flag] = pcg(S,b);
223 % nonsymmetric solvers:
224 % [U, flag] = bicgstab(S,b,[],1000); => 65sec
225 % [U, flag] = cgs(S,b,[],1000); % => 47 sec
226 % symmetric solver, non pd:
227 [U, flag] = symmlq(S,b,[],1000); % => 31 sec
228 % [U, flag] = minres(S,b,[],1000); % => 31 sec
229 % symmetric solver, pd:
230 % [U, flag] = pcg(S,b,[],1000); % => 46 sec
232 disp(['error in system solution, solver return flag = ', ...
237 % add dirichlet boundary values
238 U = U + dirichlet_fct;
240 % TO BE ADJUSTED TO NEW SYNTAX
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 Udirichlet = dirichlet_values(model, X, Y)
UDIRICHLET = DIRICHLET_VALUES([X],[Y], MODEL) Examples dirichlet_values([0,1,2],[1,1,1],struct(name_dirichlet_values, homogeneous, ... c_dir, 1)) dirichlet_values([0:0.1:1],[0],struct(name_dirichlet_values, xstripes, ... c_dir, [0 1 2], ... dir_borders, [0.3 0.6])) dirichlet_values([0:0.1:1],[0],struct(name_dirichlet_values, box, ... c_dir, 1, ... dir_box_xrange, [0.3 0.6], ... dir_box_yrange, [-0.1 0.1]))
A cartesian rectangular grid in two dimensions with axis parallel elements.