comparison +scheme/Elastic2dVariable.m @ 1331:60c875c18de3 feature/D2_boundary_opt

Merge with feature/poroelastic for Elastic schemes
author Vidar Stiernström <vidar.stiernstrom@it.uu.se>
date Thu, 10 Mar 2022 16:54:26 +0100
parents 19d821ddc108 70939ea9a71f
children
comparison
equal deleted inserted replaced
1330:855871e0b852 1331:60c875c18de3
12 grid 12 grid
13 dim 13 dim
14 14
15 order % Order of accuracy for the approximation 15 order % Order of accuracy for the approximation
16 16
17 % Diagonal matrices for varible coefficients 17 % Diagonal matrices for variable coefficients
18 LAMBDA % Variable coefficient, related to dilation 18 LAMBDA % Lame's first parameter, related to dilation
19 MU % Shear modulus, variable coefficient 19 MU % Shear modulus
20 RHO, RHOi % Density, variable 20 RHO, RHOi, RHOi_kron % Density
21 21
22 D % Total operator 22 D % Total operator
23 D1 % First derivatives 23 D1 % First derivatives
24 24
25 % Second derivatives 25 % Second derivatives
26 D2_lambda 26 D2_lambda
27 D2_mu 27 D2_mu
28 28
29 % Traction operators used for BC 29 % Boundary operators in cell format, used for BC
30 T_l, T_r 30 T_w, T_e, T_s, T_n
31 tau_l, tau_r 31
32 32 % Traction operators
33 H, Hi, H_1D % Inner products 33 tau_w, tau_e, tau_s, tau_n % Return vector field
34 e_l, e_r 34 tau1_w, tau1_e, tau1_s, tau1_n % Return scalar field
35 35 tau2_w, tau2_e, tau2_s, tau2_n % Return scalar field
36 36
37 d1_l, d1_r % Normal derivatives at the boundary 37 % Inner products
38 E % E{i}^T picks out component i 38 H, Hi, Hi_kron, H_1D
39 39
40 H_boundary % Boundary inner products 40 % Boundary inner products (for scalar field)
41 41 H_w, H_e, H_s, H_n
42 % Kroneckered norms and coefficients 42
43 RHOi_kron 43 % Boundary restriction operators
44 Hi_kron 44 e_w, e_e, e_s, e_n % Act on vector field, return vector field at boundary
45 e1_w, e1_e, e1_s, e1_n % Act on vector field, return scalar field at boundary
46 e2_w, e2_e, e2_s, e2_n % Act on vector field, return scalar field at boundary
47 e_scalar_w, e_scalar_e, e_scalar_s, e_scalar_n; % Act on scalar field, return scalar field
48
49 % E{i}^T picks out component i
50 E
45 51
46 % Borrowing constants of the form gamma*h, where gamma is a dimensionless constant. 52 % Borrowing constants of the form gamma*h, where gamma is a dimensionless constant.
47 theta_R % Borrowing (d1- D1)^2 from R 53 theta_R % Borrowing (d1- D1)^2 from R
48 theta_H % First entry in norm matrix 54 theta_H % First entry in norm matrix
49 theta_M % Borrowing d1^2 from M. 55 theta_M % Borrowing d1^2 from M.
53 end 59 end
54 60
55 methods 61 methods
56 62
57 % The coefficients can either be function handles or grid functions 63 % The coefficients can either be function handles or grid functions
58 function obj = Elastic2dVariable(g ,order, lambda, mu, rho, opSet) 64 % optFlag -- if true, extra computations are performed, which may be helpful for optimization.
65 function obj = Elastic2dVariable(g ,order, lambda, mu, rho, opSet, optFlag)
59 default_arg('opSet',{@sbp.D2Variable, @sbp.D2Variable}); 66 default_arg('opSet',{@sbp.D2Variable, @sbp.D2Variable});
60 default_arg('lambda', @(x,y) 0*x+1); 67 default_arg('lambda', @(x,y) 0*x+1);
61 default_arg('mu', @(x,y) 0*x+1); 68 default_arg('mu', @(x,y) 0*x+1);
62 default_arg('rho', @(x,y) 0*x+1); 69 default_arg('rho', @(x,y) 0*x+1);
70 default_arg('optFlag', false);
63 dim = 2; 71 dim = 2;
64 72
65 assert(isa(g, 'grid.Cartesian')) 73 assert(isa(g, 'grid.Cartesian'))
66 74
67 if isa(lambda, 'function_handle') 75 if isa(lambda, 'function_handle')
103 I = cell(dim,1); 111 I = cell(dim,1);
104 D1 = cell(dim,1); 112 D1 = cell(dim,1);
105 D2 = cell(dim,1); 113 D2 = cell(dim,1);
106 H = cell(dim,1); 114 H = cell(dim,1);
107 Hi = cell(dim,1); 115 Hi = cell(dim,1);
108 e_l = cell(dim,1); 116 e_0 = cell(dim,1);
109 e_r = cell(dim,1); 117 e_m = cell(dim,1);
110 d1_l = cell(dim,1); 118 d1_0 = cell(dim,1);
111 d1_r = cell(dim,1); 119 d1_m = cell(dim,1);
112 120
113 for i = 1:dim 121 for i = 1:dim
114 I{i} = speye(m(i)); 122 I{i} = speye(m(i));
115 D1{i} = ops{i}.D1; 123 D1{i} = ops{i}.D1;
116 D2{i} = ops{i}.D2; 124 D2{i} = ops{i}.D2;
117 H{i} = ops{i}.H; 125 H{i} = ops{i}.H;
118 Hi{i} = ops{i}.HI; 126 Hi{i} = ops{i}.HI;
119 e_l{i} = ops{i}.e_l; 127 e_0{i} = ops{i}.e_l;
120 e_r{i} = ops{i}.e_r; 128 e_m{i} = ops{i}.e_r;
121 d1_l{i} = ops{i}.d1_l; 129 d1_0{i} = ops{i}.d1_l;
122 d1_r{i} = ops{i}.d1_r; 130 d1_m{i} = ops{i}.d1_r;
123 end 131 end
124 132
125 %====== Assemble full operators ======== 133 %====== Assemble full operators ========
126 LAMBDA = spdiag(lambda); 134 LAMBDA = spdiag(lambda);
127 obj.LAMBDA = LAMBDA; 135 obj.LAMBDA = LAMBDA;
132 obj.RHOi = inv(RHO); 140 obj.RHOi = inv(RHO);
133 141
134 obj.D1 = cell(dim,1); 142 obj.D1 = cell(dim,1);
135 obj.D2_lambda = cell(dim,1); 143 obj.D2_lambda = cell(dim,1);
136 obj.D2_mu = cell(dim,1); 144 obj.D2_mu = cell(dim,1);
137 obj.e_l = cell(dim,1);
138 obj.e_r = cell(dim,1);
139 obj.d1_l = cell(dim,1);
140 obj.d1_r = cell(dim,1);
141 145
142 % D1 146 % D1
143 obj.D1{1} = kron(D1{1},I{2}); 147 obj.D1{1} = kron(D1{1},I{2});
144 obj.D1{2} = kron(I{1},D1{2}); 148 obj.D1{2} = kron(I{1},D1{2});
145 149
146 % Boundary operators 150 % Boundary restriction operators
147 obj.e_l{1} = kron(e_l{1},I{2}); 151 e_l = cell(dim,1);
148 obj.e_l{2} = kron(I{1},e_l{2}); 152 e_r = cell(dim,1);
149 obj.e_r{1} = kron(e_r{1},I{2}); 153 e_l{1} = kron(e_0{1}, I{2});
150 obj.e_r{2} = kron(I{1},e_r{2}); 154 e_l{2} = kron(I{1}, e_0{2});
151 155 e_r{1} = kron(e_m{1}, I{2});
152 obj.d1_l{1} = kron(d1_l{1},I{2}); 156 e_r{2} = kron(I{1}, e_m{2});
153 obj.d1_l{2} = kron(I{1},d1_l{2}); 157
154 obj.d1_r{1} = kron(d1_r{1},I{2}); 158 e_scalar_w = e_l{1};
155 obj.d1_r{2} = kron(I{1},d1_r{2}); 159 e_scalar_e = e_r{1};
156 160 e_scalar_s = e_l{2};
157 % D2 161 e_scalar_n = e_r{2};
158 for i = 1:dim 162
159 obj.D2_lambda{i} = sparse(m_tot); 163 I_dim = speye(dim, dim);
160 obj.D2_mu{i} = sparse(m_tot); 164 e_w = kron(e_scalar_w, I_dim);
161 end 165 e_e = kron(e_scalar_e, I_dim);
162 ind = grid.funcToMatrix(g, 1:m_tot); 166 e_s = kron(e_scalar_s, I_dim);
163 167 e_n = kron(e_scalar_n, I_dim);
164 for i = 1:m(2) 168
165 D_lambda = D2{1}(lambda(ind(:,i))); 169 % Boundary derivatives
166 D_mu = D2{1}(mu(ind(:,i))); 170 d1_l = cell(dim,1);
167 171 d1_r = cell(dim,1);
168 p = ind(:,i); 172 d1_l{1} = kron(d1_0{1}, I{2});
169 obj.D2_lambda{1}(p,p) = D_lambda; 173 d1_l{2} = kron(I{1}, d1_0{2});
170 obj.D2_mu{1}(p,p) = D_mu; 174 d1_r{1} = kron(d1_m{1}, I{2});
171 end 175 d1_r{2} = kron(I{1}, d1_m{2});
172 176
173 for i = 1:m(1)
174 D_lambda = D2{2}(lambda(ind(i,:)));
175 D_mu = D2{2}(mu(ind(i,:)));
176
177 p = ind(i,:);
178 obj.D2_lambda{2}(p,p) = D_lambda;
179 obj.D2_mu{2}(p,p) = D_mu;
180 end
181
182 % Quadratures
183 obj.H = kron(H{1},H{2});
184 obj.Hi = inv(obj.H);
185 obj.H_boundary = cell(dim,1);
186 obj.H_boundary{1} = H{2};
187 obj.H_boundary{2} = H{1};
188 obj.H_1D = {H{1}, H{2}};
189 177
190 % E{i}^T picks out component i. 178 % E{i}^T picks out component i.
191 E = cell(dim,1); 179 E = cell(dim,1);
192 I = speye(m_tot,m_tot); 180 I = speye(m_tot,m_tot);
193 for i = 1:dim 181 for i = 1:dim
194 e = sparse(dim,1); 182 e = sparse(dim,1);
195 e(i) = 1; 183 e(i) = 1;
196 E{i} = kron(I,e); 184 E{i} = kron(I,e);
197 end 185 end
198 obj.E = E; 186 obj.E = E;
187
188 e1_w = (e_scalar_w'*E{1}')';
189 e1_e = (e_scalar_e'*E{1}')';
190 e1_s = (e_scalar_s'*E{1}')';
191 e1_n = (e_scalar_n'*E{1}')';
192
193 e2_w = (e_scalar_w'*E{2}')';
194 e2_e = (e_scalar_e'*E{2}')';
195 e2_s = (e_scalar_s'*E{2}')';
196 e2_n = (e_scalar_n'*E{2}')';
197
198
199 % D2
200 for i = 1:dim
201 obj.D2_lambda{i} = sparse(m_tot, m_tot);
202 obj.D2_mu{i} = sparse(m_tot, m_tot);
203 end
204 ind = grid.funcToMatrix(g, 1:m_tot);
205
206 for i = 1:m(2)
207 D_lambda = D2{1}(lambda(ind(:,i)));
208 D_mu = D2{1}(mu(ind(:,i)));
209
210 p = ind(:,i);
211 obj.D2_lambda{1}(p,p) = D_lambda;
212 obj.D2_mu{1}(p,p) = D_mu;
213 end
214
215 for i = 1:m(1)
216 D_lambda = D2{2}(lambda(ind(i,:)));
217 D_mu = D2{2}(mu(ind(i,:)));
218
219 p = ind(i,:);
220 obj.D2_lambda{2}(p,p) = D_lambda;
221 obj.D2_mu{2}(p,p) = D_mu;
222 end
223
224 % Quadratures
225 obj.H = kron(H{1},H{2});
226 obj.Hi = inv(obj.H);
227 obj.H_w = H{2};
228 obj.H_e = H{2};
229 obj.H_s = H{1};
230 obj.H_n = H{1};
231 obj.H_1D = {H{1}, H{2}};
199 232
200 % Differentiation matrix D (without SAT) 233 % Differentiation matrix D (without SAT)
201 D2_lambda = obj.D2_lambda; 234 D2_lambda = obj.D2_lambda;
202 D2_mu = obj.D2_mu; 235 D2_mu = obj.D2_mu;
203 D1 = obj.D1; 236 D1 = obj.D1;
219 %=========================================%' 252 %=========================================%'
220 253
221 % Numerical traction operators for BC. 254 % Numerical traction operators for BC.
222 % Because d1 =/= e0^T*D1, the numerical tractions are different 255 % Because d1 =/= e0^T*D1, the numerical tractions are different
223 % at every boundary. 256 % at every boundary.
257 %
258 % Formula at boundary j: % tau^{j}_i = sum_k T^{j}_{ik} u_k
259 %
224 T_l = cell(dim,1); 260 T_l = cell(dim,1);
225 T_r = cell(dim,1); 261 T_r = cell(dim,1);
226 tau_l = cell(dim,1); 262 tau_l = cell(dim,1);
227 tau_r = cell(dim,1); 263 tau_r = cell(dim,1);
228 % tau^{j}_i = sum_k T^{j}_{ik} u_k 264
229
230 d1_l = obj.d1_l;
231 d1_r = obj.d1_r;
232 e_l = obj.e_l;
233 e_r = obj.e_r;
234 D1 = obj.D1; 265 D1 = obj.D1;
235 266
236 % Loop over boundaries 267 % Loop over boundaries
237 for j = 1:dim 268 for j = 1:dim
238 T_l{j} = cell(dim,dim); 269 T_l{j} = cell(dim,dim);
248 [~, n_l] = size(e_l{j}); 279 [~, n_l] = size(e_l{j});
249 [~, n_r] = size(e_r{j}); 280 [~, n_r] = size(e_r{j});
250 281
251 % Loop over components 282 % Loop over components
252 for i = 1:dim 283 for i = 1:dim
253 tau_l{j}{i} = sparse(n_l, dim*m_tot); 284 tau_l{j}{i} = sparse(dim*m_tot, n_l);
254 tau_r{j}{i} = sparse(n_r, dim*m_tot); 285 tau_r{j}{i} = sparse(dim*m_tot, n_r);
255 for k = 1:dim 286 for k = 1:dim
256 T_l{j}{i,k} = ... 287 T_l{j}{i,k} = ...
257 -d(i,j)*LAMBDA_l*(d(i,k)*d1_l{j}' + db(i,k)*e_l{j}'*D1{k})... 288 (-d(i,j)*LAMBDA_l*(d(i,k)*d1_l{j}' + db(i,k)*e_l{j}'*D1{k})...
258 -d(j,k)*MU_l*(d(i,j)*d1_l{j}' + db(i,j)*e_l{j}'*D1{i})... 289 -d(j,k)*MU_l*(d(i,j)*d1_l{j}' + db(i,j)*e_l{j}'*D1{i})...
259 -d(i,k)*MU_l*d1_l{j}'; 290 -d(i,k)*MU_l*d1_l{j}')';
260 291
261 T_r{j}{i,k} = ... 292 T_r{j}{i,k} = ...
262 d(i,j)*LAMBDA_r*(d(i,k)*d1_r{j}' + db(i,k)*e_r{j}'*D1{k})... 293 (d(i,j)*LAMBDA_r*(d(i,k)*d1_r{j}' + db(i,k)*e_r{j}'*D1{k})...
263 +d(j,k)*MU_r*(d(i,j)*d1_r{j}' + db(i,j)*e_r{j}'*D1{i})... 294 +d(j,k)*MU_r*(d(i,j)*d1_r{j}' + db(i,j)*e_r{j}'*D1{i})...
264 +d(i,k)*MU_r*d1_r{j}'; 295 +d(i,k)*MU_r*d1_r{j}')';
265 296
266 tau_l{j}{i} = tau_l{j}{i} + T_l{j}{i,k}*E{k}'; 297 tau_l{j}{i} = tau_l{j}{i} + (T_l{j}{i,k}'*E{k}')';
267 tau_r{j}{i} = tau_r{j}{i} + T_r{j}{i,k}*E{k}'; 298 tau_r{j}{i} = tau_r{j}{i} + (T_r{j}{i,k}'*E{k}')';
268 end 299 end
269 300
270 end 301 end
271 end 302 end
272 303
273 % Transpose T and tau to match boundary operator convention 304 % Traction tensors, T_ij
274 for i = 1:dim 305 obj.T_w = T_l{1};
275 for j = 1:dim 306 obj.T_e = T_r{1};
276 tau_l{i}{j} = transpose(tau_l{i}{j}); 307 obj.T_s = T_l{2};
277 tau_r{i}{j} = transpose(tau_r{i}{j}); 308 obj.T_n = T_r{2};
278 for k = 1:dim 309
279 T_l{i}{j,k} = transpose(T_l{i}{j,k}); 310 % Restriction operators
280 T_r{i}{j,k} = transpose(T_r{i}{j,k}); 311 obj.e_w = e_w;
281 end 312 obj.e_e = e_e;
282 end 313 obj.e_s = e_s;
283 end 314 obj.e_n = e_n;
284 315
285 obj.T_l = T_l; 316 obj.e1_w = e1_w;
286 obj.T_r = T_r; 317 obj.e1_e = e1_e;
287 obj.tau_l = tau_l; 318 obj.e1_s = e1_s;
288 obj.tau_r = tau_r; 319 obj.e1_n = e1_n;
320
321 obj.e2_w = e2_w;
322 obj.e2_e = e2_e;
323 obj.e2_s = e2_s;
324 obj.e2_n = e2_n;
325
326 obj.e_scalar_w = e_scalar_w;
327 obj.e_scalar_e = e_scalar_e;
328 obj.e_scalar_s = e_scalar_s;
329 obj.e_scalar_n = e_scalar_n;
330
331 % First component of traction
332 obj.tau1_w = tau_l{1}{1};
333 obj.tau1_e = tau_r{1}{1};
334 obj.tau1_s = tau_l{2}{1};
335 obj.tau1_n = tau_r{2}{1};
336
337 % Second component of traction
338 obj.tau2_w = tau_l{1}{2};
339 obj.tau2_e = tau_r{1}{2};
340 obj.tau2_s = tau_l{2}{2};
341 obj.tau2_n = tau_r{2}{2};
342
343 % Traction vectors
344 obj.tau_w = (e_w'*e1_w*obj.tau1_w')' + (e_w'*e2_w*obj.tau2_w')';
345 obj.tau_e = (e_e'*e1_e*obj.tau1_e')' + (e_e'*e2_e*obj.tau2_e')';
346 obj.tau_s = (e_s'*e1_s*obj.tau1_s')' + (e_s'*e2_s*obj.tau2_s')';
347 obj.tau_n = (e_n'*e1_n*obj.tau1_n')' + (e_n'*e2_n*obj.tau2_n')';
289 348
290 % Kroneckered norms and coefficients 349 % Kroneckered norms and coefficients
291 I_dim = speye(dim);
292 obj.RHOi_kron = kron(obj.RHOi, I_dim); 350 obj.RHOi_kron = kron(obj.RHOi, I_dim);
293 obj.Hi_kron = kron(obj.Hi, I_dim); 351 obj.Hi_kron = kron(obj.Hi, I_dim);
294 352
295 % Misc. 353 % Misc.
296 obj.m = m; 354 obj.m = m;
298 obj.order = order; 356 obj.order = order;
299 obj.grid = g; 357 obj.grid = g;
300 obj.dim = dim; 358 obj.dim = dim;
301 359
302 % B, used for adjoint optimization 360 % B, used for adjoint optimization
303 B = cell(dim, 1); 361 B = [];
304 for i = 1:dim 362 if optFlag
305 B{i} = cell(m_tot, 1); 363 B = cell(dim, 1);
306 end 364 for i = 1:dim
307 365 B{i} = cell(m_tot, 1);
308 for i = 1:dim 366 end
309 for j = 1:m_tot 367
310 B{i}{j} = sparse(m_tot, m_tot); 368 B0 = sparse(m_tot, m_tot);
311 end 369 for i = 1:dim
312 end 370 for j = 1:m_tot
313 371 B{i}{j} = B0;
314 ind = grid.funcToMatrix(g, 1:m_tot); 372 end
315 373 end
316 % Direction 1 374
317 for k = 1:m(1) 375 ind = grid.funcToMatrix(g, 1:m_tot);
318 c = sparse(m(1),1); 376
319 c(k) = 1; 377 % Direction 1
320 [~, B_1D] = ops{1}.D2(c); 378 for k = 1:m(1)
321 for l = 1:m(2) 379 c = sparse(m(1),1);
322 p = ind(:,l); 380 c(k) = 1;
323 B{1}{(k-1)*m(2) + l}(p, p) = B_1D; 381 [~, B_1D] = ops{1}.D2(c);
324 end 382 for l = 1:m(2)
325 end 383 p = ind(:,l);
326 384 B{1}{(k-1)*m(2) + l}(p, p) = B_1D;
327 % Direction 2 385 end
328 for k = 1:m(2) 386 end
329 c = sparse(m(2),1); 387
330 c(k) = 1; 388 % Direction 2
331 [~, B_1D] = ops{2}.D2(c); 389 for k = 1:m(2)
332 for l = 1:m(1) 390 c = sparse(m(2),1);
333 p = ind(l,:); 391 c(k) = 1;
334 B{2}{(l-1)*m(2) + k}(p, p) = B_1D; 392 [~, B_1D] = ops{2}.D2(c);
335 end 393 for l = 1:m(1)
336 end 394 p = ind(l,:);
337 395 B{2}{(l-1)*m(2) + k}(p, p) = B_1D;
396 end
397 end
398 end
338 obj.B = B; 399 obj.B = B;
400
339 401
340 end 402 end
341 403
342 404
343 % Closure functions return the operators applied to the own domain to close the boundary 405 % Closure functions return the operators applied to the own domain to close the boundary
344 % Penalty functions return the operators to force the solution. In the case of an interface it returns the operator applied to the other doamin. 406 % Penalty functions return the operators to force the solution. In the case of an interface it returns the operator applied to the other doamin.
345 % boundary is a string specifying the boundary e.g. 'l','r' or 'e','w','n','s'. 407 % boundary is a string specifying the boundary e.g. 'l','r' or 'e','w','n','s'.
346 % bc is a cell array of component and bc type, e.g. {1, 'd'} for Dirichlet condition 408 % bc is a cell array of component and bc type, e.g. {1, 'd'} for Dirichlet condition
347 % on the first component. 409 % on the first component. Can also be e.g.
410 % {'normal', 'd'} or {'tangential', 't'} for conditions on
411 % tangential/normal component.
348 % data is a function returning the data that should be applied at the boundary. 412 % data is a function returning the data that should be applied at the boundary.
349 % neighbour_scheme is an instance of Scheme that should be interfaced to. 413 % neighbour_scheme is an instance of Scheme that should be interfaced to.
350 % neighbour_boundary is a string specifying which boundary to interface to. 414 % neighbour_boundary is a string specifying which boundary to interface to.
351 function [closure, penalty] = boundary_condition(obj, boundary, bc, tuning) 415 function [closure, penalty] = boundary_condition(obj, boundary, bc, tuning)
352 default_arg('tuning', 1.2); 416 default_arg('tuning', 1.2);
353 417
354 assert( iscell(bc), 'The BC type must be a 2x1 cell array' ); 418 assert( iscell(bc), 'The BC type must be a 2x1 cell array' );
355 comp = bc{1}; 419 comp = bc{1};
356 type = bc{2}; 420 type = bc{2};
357 421 if ischar(comp)
358 % j is the coordinate direction of the boundary 422 comp = obj.getComponent(comp, boundary);
359 j = obj.get_boundary_number(boundary); 423 end
360 [e, T, tau, H_gamma] = obj.getBoundaryOperator({'e','T','tau','H'}, boundary); 424
361 425 e = obj.getBoundaryOperatorForScalarField('e', boundary);
426 tau = obj.getBoundaryOperator(['tau' num2str(comp)], boundary);
427 T = obj.getBoundaryTractionOperator(boundary);
428 alpha = obj.getBoundaryOperatorForScalarField('alpha', boundary);
429 H_gamma = obj.getBoundaryQuadratureForScalarField(boundary);
362 430
363 E = obj.E; 431 E = obj.E;
364 Hi = obj.Hi; 432 Hi = obj.Hi;
365 LAMBDA = obj.LAMBDA; 433 LAMBDA = obj.LAMBDA;
366 MU = obj.MU; 434 MU = obj.MU;
368 436
369 dim = obj.dim; 437 dim = obj.dim;
370 m_tot = obj.grid.N(); 438 m_tot = obj.grid.N();
371 439
372 % Preallocate 440 % Preallocate
441 [~, col] = size(tau);
373 closure = sparse(dim*m_tot, dim*m_tot); 442 closure = sparse(dim*m_tot, dim*m_tot);
374 penalty = sparse(dim*m_tot, m_tot/obj.m(j)); 443 penalty = sparse(dim*m_tot, col);
375 444
376 k = comp; 445 k = comp;
377 switch type 446 switch type
378 447
379 % Dirichlet boundary condition 448 % Dirichlet boundary condition
380 case {'D','d','dirichlet','Dirichlet'} 449 case {'D','d','dirichlet','Dirichlet'}
381
382 alpha = obj.getBoundaryOperator('alpha', boundary);
383 450
384 % Loop over components that Dirichlet penalties end up on 451 % Loop over components that Dirichlet penalties end up on
385 for i = 1:dim 452 for i = 1:dim
386 C = transpose(T{k,i}); 453 C = transpose(T{k,i});
387 A = -tuning*e*transpose(alpha{i,k}); 454 A = -tuning*e*transpose(alpha{i,k});
390 penalty = penalty - E{i}*RHOi*Hi*B'*e*H_gamma; 457 penalty = penalty - E{i}*RHOi*Hi*B'*e*H_gamma;
391 end 458 end
392 459
393 % Free boundary condition 460 % Free boundary condition
394 case {'F','f','Free','free','traction','Traction','t','T'} 461 case {'F','f','Free','free','traction','Traction','t','T'}
395 closure = closure - E{k}*RHOi*Hi*e*H_gamma*tau{k}'; 462 closure = closure - E{k}*RHOi*Hi*e*H_gamma*tau';
396 penalty = penalty + E{k}*RHOi*Hi*e*H_gamma; 463 penalty = penalty + E{k}*RHOi*Hi*e*H_gamma;
397 464
398 % Unknown boundary condition 465 % Unknown boundary condition
399 otherwise 466 otherwise
400 error('No such boundary condition: type = %s',type); 467 error('No such boundary condition: type = %s',type);
427 % u denotes the solution in the own domain 494 % u denotes the solution in the own domain
428 % v denotes the solution in the neighbour domain 495 % v denotes the solution in the neighbour domain
429 % Operators without subscripts are from the own domain. 496 % Operators without subscripts are from the own domain.
430 497
431 % Get boundary operators 498 % Get boundary operators
432 e = obj.getBoundaryOperator('e_tot', boundary); 499 e = obj.getBoundaryOperator('e', boundary);
433 tau = obj.getBoundaryOperator('tau_tot', boundary); 500 tau = obj.getBoundaryOperator('tau', boundary);
434 501
435 e_v = neighbour_scheme.getBoundaryOperator('e_tot', neighbour_boundary); 502 e_v = neighbour_scheme.getBoundaryOperator('e', neighbour_boundary);
436 tau_v = neighbour_scheme.getBoundaryOperator('tau_tot', neighbour_boundary); 503 tau_v = neighbour_scheme.getBoundaryOperator('tau', neighbour_boundary);
437 504
438 H_gamma = obj.getBoundaryQuadrature(boundary); 505 H_gamma = obj.getBoundaryQuadrature(boundary);
439 506
440 % Operators and quantities that correspond to the own domain only 507 % Operators and quantities that correspond to the own domain only
441 Hi = obj.Hi_kron; 508 Hi = obj.Hi_kron;
442 RHOi = obj.RHOi_kron; 509 RHOi = obj.RHOi_kron;
443 510
444 % Penalty strength operators 511 % Penalty strength operators
445 alpha_u = 1/4*tuning*obj.getBoundaryOperator('alpha_tot', boundary); 512 alpha_u = 1/4*tuning*obj.getBoundaryOperator('alpha', boundary);
446 alpha_v = 1/4*tuning*neighbour_scheme.getBoundaryOperator('alpha_tot', neighbour_boundary); 513 alpha_v = 1/4*tuning*neighbour_scheme.getBoundaryOperator('alpha', neighbour_boundary);
447 514
448 closure = -RHOi*Hi*e*H_gamma*(alpha_u' + alpha_v'*e_v*e'); 515 closure = -RHOi*Hi*e*H_gamma*(alpha_u' + alpha_v'*e_v*e');
449 penalty = RHOi*Hi*e*H_gamma*(alpha_u'*e*e_v' + alpha_v'); 516 penalty = RHOi*Hi*e*H_gamma*(alpha_u'*e*e_v' + alpha_v');
450 517
451 closure = closure - 1/2*RHOi*Hi*e*H_gamma*tau'; 518 closure = closure - 1/2*RHOi*Hi*e*H_gamma*tau';
458 525
459 function [closure, penalty] = interfaceNonConforming(obj,boundary,neighbour_scheme,neighbour_boundary,type) 526 function [closure, penalty] = interfaceNonConforming(obj,boundary,neighbour_scheme,neighbour_boundary,type)
460 error('Non-conforming interfaces not implemented yet.'); 527 error('Non-conforming interfaces not implemented yet.');
461 end 528 end
462 529
463 % Returns the coordinate number and outward normal component for the boundary specified by the string boundary. 530 % Returns the component number that is the tangential/normal component
464 function [j, nj] = get_boundary_number(obj, boundary) 531 % at the specified boundary
465 assertIsMember(boundary, {'w', 'e', 's', 'n'}) 532 function comp = getComponent(obj, comp_str, boundary)
533 assertIsMember(comp_str, {'normal', 'tangential'});
534 assertIsMember(boundary, {'w', 'e', 's', 'n'});
466 535
467 switch boundary 536 switch boundary
468 case {'w', 'e'} 537 case {'w', 'e'}
469 j = 1; 538 switch comp_str
470 case {'s', 'n'} 539 case 'normal'
471 j = 2; 540 comp = 1;
472 end 541 case 'tangential'
473 542 comp = 2;
474 switch boundary 543 end
475 case {'w', 's'} 544 case {'s', 'n'}
476 nj = -1; 545 switch comp_str
477 case {'e', 'n'} 546 case 'normal'
478 nj = 1; 547 comp = 2;
548 case 'tangential'
549 comp = 1;
550 end
479 end 551 end
480 end 552 end
481 553
482 % Returns the boundary operator op for the boundary specified by the string boundary. 554 % Returns the boundary operator op for the boundary specified by the string boundary.
483 % op -- string 555 % op -- string
484 % Only operators with name *_tot can be used with multiblock.DiffOp.getBoundaryOperator() 556 function o = getBoundaryOperator(obj, op, boundary)
485 function [varargout] = getBoundaryOperator(obj, op, boundary)
486 assertIsMember(boundary, {'w', 'e', 's', 'n'}) 557 assertIsMember(boundary, {'w', 'e', 's', 'n'})
487 assertIsMember(op, {'e', 'e_tot', 'd', 'T', 'tau', 'tau_tot', 'H', 'alpha', 'alpha_tot'}) 558 assertIsMember(op, {'e', 'e1', 'e2', 'tau', 'tau1', 'tau2', 'alpha', 'alpha1', 'alpha2'})
488
489 switch boundary
490 case {'w', 'e'}
491 j = 1;
492 case {'s', 'n'}
493 j = 2;
494 end
495 559
496 switch op 560 switch op
561
562 case {'e', 'e1', 'e2', 'tau', 'tau1', 'tau2'}
563 o = obj.([op, '_', boundary]);
564
565 % Yields vector-valued penalty strength given displacement BC on all components
566 case 'alpha'
567 e = obj.getBoundaryOperator('e', boundary);
568 e_scalar = obj.getBoundaryOperatorForScalarField('e', boundary);
569 alpha_scalar = obj.getBoundaryOperatorForScalarField('alpha', boundary);
570 E = obj.E;
571 [m, n] = size(alpha_scalar{1,1});
572 alpha = sparse(m*obj.dim, n*obj.dim);
573 for i = 1:obj.dim
574 for l = 1:obj.dim
575 alpha = alpha + (e'*E{i}*e_scalar*alpha_scalar{i,l}'*E{l}')';
576 end
577 end
578 o = alpha;
579
580 % Yields penalty strength for component 1 given displacement BC on all components
581 case 'alpha1'
582 alpha = obj.getBoundaryOperator('alpha', boundary);
583 e = obj.getBoundaryOperator('e', boundary);
584 e1 = obj.getBoundaryOperator('e1', boundary);
585
586 alpha1 = (e1'*e*alpha')';
587 o = alpha1;
588
589 % Yields penalty strength for component 2 given displacement BC on all components
590 case 'alpha2'
591 alpha = obj.getBoundaryOperator('alpha', boundary);
592 e = obj.getBoundaryOperator('e', boundary);
593 e2 = obj.getBoundaryOperator('e2', boundary);
594
595 alpha2 = (e2'*e*alpha')';
596 o = alpha2;
597 end
598
599 end
600
601 % Returns the boundary operator op for the boundary specified by the string boundary.
602 % op -- string
603 function o = getBoundaryOperatorForScalarField(obj, op, boundary)
604 assertIsMember(boundary, {'w', 'e', 's', 'n'})
605 assertIsMember(op, {'e', 'alpha'})
606
607 switch op
608
497 case 'e' 609 case 'e'
498 switch boundary 610 o = obj.(['e_scalar', '_', boundary]);
499 case {'w', 's'}
500 o = obj.e_l{j};
501 case {'e', 'n'}
502 o = obj.e_r{j};
503 end
504
505 case 'e_tot'
506 e = obj.getBoundaryOperator('e', boundary);
507 I_dim = speye(obj.dim, obj.dim);
508 o = kron(e, I_dim);
509
510 case 'd'
511 switch boundary
512 case {'w', 's'}
513 o = obj.d1_l{j};
514 case {'e', 'n'}
515 o = obj.d1_r{j};
516 end
517
518 case 'T'
519 switch boundary
520 case {'w', 's'}
521 o = obj.T_l{j};
522 case {'e', 'n'}
523 o = obj.T_r{j};
524 end
525
526 case 'tau'
527 switch boundary
528 case {'w', 's'}
529 o = obj.tau_l{j};
530 case {'e', 'n'}
531 o = obj.tau_r{j};
532 end
533
534 case 'tau_tot'
535 [e, tau] = obj.getBoundaryOperator({'e', 'tau'}, boundary);
536
537 I_dim = speye(obj.dim, obj.dim);
538 e_tot = kron(e, I_dim);
539 E = obj.E;
540 tau_tot = (e_tot'*E{1}*e*tau{1}')';
541 for i = 2:obj.dim
542 tau_tot = tau_tot + (e_tot'*E{i}*e*tau{i}')';
543 end
544 o = tau_tot;
545
546 case 'H'
547 o = obj.H_boundary{j};
548 611
549 case 'alpha' 612 case 'alpha'
550 % alpha = alpha(i,j) is the penalty strength for displacement BC. 613
551 e = obj.getBoundaryOperator('e', boundary); 614 % alpha{i,j} is the penalty strength on component i due to
615 % displacement BC for component j.
616 e = obj.getBoundaryOperatorForScalarField('e', boundary);
552 617
553 LAMBDA = obj.LAMBDA; 618 LAMBDA = obj.LAMBDA;
554 MU = obj.MU; 619 MU = obj.MU;
555
556 dim = obj.dim; 620 dim = obj.dim;
557 theta_R = obj.theta_R{j}; 621
558 theta_H = obj.theta_H{j}; 622 switch boundary
559 theta_M = obj.theta_M{j}; 623 case {'w', 'e'}
624 k = 1;
625 case {'s', 'n'}
626 k = 2;
627 end
628
629 theta_R = obj.theta_R{k};
630 theta_H = obj.theta_H{k};
631 theta_M = obj.theta_M{k};
560 632
561 a_lambda = dim/theta_H + 1/theta_R; 633 a_lambda = dim/theta_H + 1/theta_R;
562 a_mu_i = 2/theta_M; 634 a_mu_i = 2/theta_M;
563 a_mu_ij = 2/theta_H + 1/theta_R; 635 a_mu_ij = 2/theta_H + 1/theta_R;
564 636
565 d = @kroneckerDelta; % Kronecker delta 637 d = @kroneckerDelta; % Kronecker delta
566 db = @(i,j) 1-d(i,j); % Logical not of Kronecker delta 638 db = @(i,j) 1-d(i,j); % Logical not of Kronecker delta
567 alpha = cell(obj.dim, obj.dim);
568 639
569 alpha_func = @(i,j) d(i,j)* a_lambda*LAMBDA ... 640 alpha_func = @(i,j) d(i,j)* a_lambda*LAMBDA ...
570 + d(i,j)* a_mu_i*MU ... 641 + d(i,j)* a_mu_i*MU ...
571 + db(i,j)*a_mu_ij*MU; 642 + db(i,j)*a_mu_ij*MU;
643
644 alpha = cell(obj.dim, obj.dim);
572 for i = 1:obj.dim 645 for i = 1:obj.dim
573 for l = 1:obj.dim 646 for j = 1:obj.dim
574 alpha{i,l} = d(i,l)*alpha_func(i,j)*e; 647 alpha{i,j} = d(i,j)*alpha_func(i,k)*e;
575 end 648 end
576 end 649 end
577
578 o = alpha; 650 o = alpha;
579 651 end
580 case 'alpha_tot' 652
581 % alpha = alpha(i,j) is the penalty strength for displacement BC. 653 end
582 [e, e_tot, alpha] = obj.getBoundaryOperator({'e', 'e_tot', 'alpha'}, boundary); 654
583 E = obj.E; 655 % Returns the boundary operator T_ij (cell format) for the boundary specified by the string boundary.
584 [m, n] = size(alpha{1,1}); 656 % Formula: tau_i = T_ij u_j
585 alpha_tot = sparse(m*obj.dim, n*obj.dim); 657 % op -- string
586 for i = 1:obj.dim 658 function T = getBoundaryTractionOperator(obj, boundary)
587 for l = 1:obj.dim 659 assertIsMember(boundary, {'w', 'e', 's', 'n'})
588 alpha_tot = alpha_tot + (e_tot'*E{i}*e*alpha{i,l}'*E{l}')'; 660
589 end 661 T = obj.(['T', '_', boundary]);
590 end
591 o = alpha_tot;
592 end
593
594 end 662 end
595 663
596 % Returns square boundary quadrature matrix, of dimension 664 % Returns square boundary quadrature matrix, of dimension
597 % corresponding to the number of boundary points 665 % corresponding to the number of boundary unknowns
598 % 666 %
599 % boundary -- string 667 % boundary -- string
600 function H = getBoundaryQuadrature(obj, boundary) 668 function H = getBoundaryQuadrature(obj, boundary)
601 assertIsMember(boundary, {'w', 'e', 's', 'n'}) 669 assertIsMember(boundary, {'w', 'e', 's', 'n'})
602 670
603 switch boundary 671 H = obj.getBoundaryQuadratureForScalarField(boundary);
604 case {'w','e'}
605 j = 1;
606 case {'s','n'}
607 j = 2;
608 end
609 H = obj.H_boundary{j};
610 I_dim = speye(obj.dim, obj.dim); 672 I_dim = speye(obj.dim, obj.dim);
611 H = kron(H, I_dim); 673 H = kron(H, I_dim);
612 end 674 end
613 675
676 % Returns square boundary quadrature matrix, of dimension
677 % corresponding to the number of boundary grid points
678 %
679 % boundary -- string
680 function H_b = getBoundaryQuadratureForScalarField(obj, boundary)
681 assertIsMember(boundary, {'w', 'e', 's', 'n'})
682
683 H_b = obj.(['H_', boundary]);
684 end
685
614 function N = size(obj) 686 function N = size(obj)
615 N = obj.dim*prod(obj.m); 687 N = obj.dim*prod(obj.m);
616 end 688 end
617 end 689 end
618 end 690 end