comparison +scheme/Elastic2dVariableAnisotropicIncompatible.m @ 1345:14f44e81e1e3 feature/poroelastic

Add scheme for Elastic2dVariable, not fully compatible.
author Martin Almquist <martin.almquist@it.uu.se>
date Tue, 30 Apr 2024 13:33:39 +0200
parents
children 464e6f65c2c6
comparison
equal deleted inserted replaced
1322:412b8ceafbc6 1345:14f44e81e1e3
1 classdef Elastic2dVariableAnisotropicIncompatible < scheme.Scheme
2
3 % Discretizes the elastic wave equation:
4 % rho u_{i,tt} = dj C_{ijkl} dk u_j
5 % opSet should be cell array of opSets, one per dimension. This
6 % is useful if we have periodic BC in one direction.
7 % Assumes fully compatible operators
8
9 properties
10 m % Number of points in each direction, possibly a vector
11 h % Grid spacing
12
13 grid
14 dim
15
16 order % Order of accuracy for the approximation
17
18 % Diagonal matrices for variable coefficients
19 RHO, RHOi, RHOi_kron % Density
20 C % Elastic stiffness tensor
21
22 D % Total operator
23 D1 % First derivatives
24 % D2 % Second derivatives
25
26 % Boundary operators in cell format, used for BC
27 T_w, T_e, T_s, T_n
28
29 % Traction operators
30 tau_w, tau_e, tau_s, tau_n % Return vector field
31 tau1_w, tau1_e, tau1_s, tau1_n % Return scalar field
32 tau2_w, tau2_e, tau2_s, tau2_n % Return scalar field
33
34 % Inner products
35 H, Hi, Hi_kron, H_1D
36
37 % Boundary inner products (for scalar field)
38 H_w, H_e, H_s, H_n
39
40 % Boundary restriction operators
41 e_w, e_e, e_s, e_n % Act on vector field, return vector field at boundary
42 e1_w, e1_e, e1_s, e1_n % Act on vector field, return scalar field at boundary
43 e2_w, e2_e, e2_s, e2_n % Act on vector field, return scalar field at boundary
44 e_scalar_w, e_scalar_e, e_scalar_s, e_scalar_n; % Act on scalar field, return scalar field
45
46 % E{i}^T picks out component i
47 E
48
49 % Borrowing constants of the form gamma*h, where gamma is a dimensionless constant.
50 h11 % First entry in norm matrix
51 theta_R % Borrowing from R
52
53 nBP % Number of boundary points, related to borrowing.
54 e_shifted_scalar_w, e_shifted_scalar_e, e_shifted_scalar_s, e_shifted_scalar_n; % Act on scalar field, return scalar field
55
56 end
57
58 methods
59
60 % The coefficients can either be function handles or grid functions
61 function obj = Elastic2dVariableAnisotropicIncompatible(g, order, rho, C, opSet, optFlag)
62 default_arg('rho', @(x,y) 0*x+1);
63 default_arg('opSet',{@sbp.D2Variable, @sbp.D2Variable});
64 dim = 2;
65
66 C_default = cell(dim,dim,dim,dim);
67 for i = 1:dim
68 for j = 1:dim
69 for k = 1:dim
70 for l = 1:dim
71 C_default{i,j,k,l} = @(x,y) 0*x + 1;
72 end
73 end
74 end
75 end
76 default_arg('C', C_default);
77 assert(isa(g, 'grid.Cartesian'))
78
79 if isa(rho, 'function_handle')
80 rho = grid.evalOn(g, rho);
81 end
82
83 C_mat = cell(dim,dim,dim,dim);
84 for i = 1:dim
85 for j = 1:dim
86 for k = 1:dim
87 for l = 1:dim
88 if isa(C{i,j,k,l}, 'function_handle')
89 C{i,j,k,l} = grid.evalOn(g, C{i,j,k,l});
90 end
91 C_mat{i,j,k,l} = spdiag(C{i,j,k,l});
92 end
93 end
94 end
95 end
96 obj.C = C_mat;
97
98 m = g.size();
99 m_tot = g.N();
100 lim = g.lim;
101 if isempty(lim)
102 x = g.x;
103 lim = cell(length(x),1);
104 for i = 1:length(x)
105 lim{i} = {min(x{i}), max(x{i})};
106 end
107 end
108
109 % 1D operators
110 ops = cell(dim,1);
111 h = zeros(dim,1);
112 for i = 1:dim
113 ops{i} = opSet{i}(m(i), lim{i}, order);
114 h(i) = ops{i}.h;
115 end
116
117 % Borrowing constants
118 for i = 1:dim
119 obj.h11{i} = h(i)*ops{i}.borrowing.H11;
120 obj.theta_R{i} = h(i)*ops{i}.borrowing.R.delta_D;
121 end
122
123 switch order
124 case 2
125 width = 3;
126 nBP = 2;
127 case 4
128 width = 5;
129 nBP = 6;
130 case 6
131 width = 7;
132 nBP = 9;
133 end
134
135 I = cell(dim,1);
136 D1 = cell(dim,1);
137 D2 = cell(dim,1);
138 H = cell(dim,1);
139 Hi = cell(dim,1);
140 e_0 = cell(dim,1);
141 e_m = cell(dim,1);
142 d1_0 = cell(dim,1);
143 d1_m = cell(dim,1);
144
145 for i = 1:dim
146 I{i} = speye(m(i));
147 D1{i} = ops{i}.D1;
148 D2{i} = ops{i}.D2;
149 H{i} = ops{i}.H;
150 Hi{i} = ops{i}.HI;
151 e_0{i} = ops{i}.e_l;
152 e_m{i} = ops{i}.e_r;
153 d1_0{i} = ops{i}.d1_l;
154 d1_m{i} = ops{i}.d1_r;
155 end
156
157 %====== Assemble full operators ========
158 I_dim = speye(dim, dim);
159 RHO = spdiag(rho);
160 obj.RHO = RHO;
161 obj.RHOi = inv(RHO);
162 obj.RHOi_kron = kron(obj.RHOi, I_dim);
163
164 obj.D1 = cell(dim,1);
165 D2_temp = cell(dim,dim,dim);
166
167 % D1
168 obj.D1{1} = kron(D1{1},I{2});
169 obj.D1{2} = kron(I{1},D1{2});
170
171 % Boundary restriction operators
172 e_l = cell(dim,1);
173 e_r = cell(dim,1);
174 e_l{1} = kron(e_0{1}, I{2});
175 e_l{2} = kron(I{1}, e_0{2});
176 e_r{1} = kron(e_m{1}, I{2});
177 e_r{2} = kron(I{1}, e_m{2});
178
179 e_scalar_w = e_l{1};
180 e_scalar_e = e_r{1};
181 e_scalar_s = e_l{2};
182 e_scalar_n = e_r{2};
183
184 e_w = kron(e_scalar_w, I_dim);
185 e_e = kron(e_scalar_e, I_dim);
186 e_s = kron(e_scalar_s, I_dim);
187 e_n = kron(e_scalar_n, I_dim);
188
189 % Boundary restriction, shifted up to nBP-1 points
190 e_shifted_scalar_w = cell(nBP, 1);
191 e_shifted_scalar_e = cell(nBP, 1);
192 e_shifted_scalar_s = cell(nBP, 1);
193 e_shifted_scalar_n = cell(nBP, 1);
194 for i = 1:nBP-1
195 e_0_nBP = {0*e_0{1}, 0*e_0{2}};
196 e_m_nBP = {0*e_m{1}, 0*e_m{2}};
197 e_0_nBP{1}(i) = 1;
198 e_0_nBP{2}(i) = 1;
199 e_m_nBP{1}(end+1-i) = 1;
200 e_m_nBP{2}(end+1-i) = 1;
201 e_shifted_scalar_w{i} = kron(e_0_nBP{1}, I{2});
202 e_shifted_scalar_s{i} = kron(I{1}, e_0_nBP{2});
203 e_shifted_scalar_e{i} = kron(e_m_nBP{1}, I{2});
204 e_shifted_scalar_n{i} = kron(I{1}, e_m_nBP{2});
205 end
206
207 % Boundary derivatives
208 d1_l = cell(dim,1);
209 d1_r = cell(dim,1);
210 d1_l{1} = kron(d1_0{1}, I{2});
211 d1_l{2} = kron(I{1}, d1_0{2});
212 d1_r{1} = kron(d1_m{1}, I{2});
213 d1_r{2} = kron(I{1}, d1_m{2});
214
215 % E{i}^T picks out component i.
216 E = cell(dim,1);
217 I = speye(m_tot,m_tot);
218 for i = 1:dim
219 e = sparse(dim,1);
220 e(i) = 1;
221 E{i} = kron(I,e);
222 end
223 obj.E = E;
224
225 e1_w = (e_scalar_w'*E{1}')';
226 e1_e = (e_scalar_e'*E{1}')';
227 e1_s = (e_scalar_s'*E{1}')';
228 e1_n = (e_scalar_n'*E{1}')';
229
230 e2_w = (e_scalar_w'*E{2}')';
231 e2_e = (e_scalar_e'*E{2}')';
232 e2_s = (e_scalar_s'*E{2}')';
233 e2_n = (e_scalar_n'*E{2}')';
234
235 % D2
236 for j = 1:dim
237 for k = 1:dim
238 for l = 1:dim
239 D2_temp{j,k,l} = spalloc(m_tot, m_tot, width*m_tot);
240 end
241 end
242 end
243 ind = grid.funcToMatrix(g, 1:m_tot);
244
245 k = 1;
246 for r = 1:m(2)
247 p = ind(:,r);
248 for j = 1:dim
249 for l = 1:dim
250 coeff = C{k,j,k,l};
251 D_kk = D2{1}(coeff(p));
252 D2_temp{j,k,l}(p,p) = D_kk;
253 end
254 end
255 end
256
257 k = 2;
258 for r = 1:m(1)
259 p = ind(r,:);
260 for j = 1:dim
261 for l = 1:dim
262 coeff = C{k,j,k,l};
263 D_kk = D2{2}(coeff(p));
264 D2_temp{j,k,l}(p,p) = D_kk;
265 end
266 end
267 end
268
269 % Quadratures
270 obj.H = kron(H{1},H{2});
271 obj.Hi = inv(obj.H);
272 obj.H_w = H{2};
273 obj.H_e = H{2};
274 obj.H_s = H{1};
275 obj.H_n = H{1};
276 obj.H_1D = {H{1}, H{2}};
277
278 % Differentiation matrix D (without SAT)
279 D1 = obj.D1;
280 D = sparse(dim*m_tot,dim*m_tot);
281 for i = 1:dim
282 for j = 1:dim
283 for k = 1:dim
284 for l = 1:dim
285 if i == k
286 D = D + E{j}*D2_temp{j,k,l}*E{l}';
287 D2_temp{j,k,l} = [];
288 else
289 D = D + E{j}*(D1{i})*C_mat{i,j,k,l}*D1{k}*E{l}';
290 end
291 end
292 end
293 end
294 end
295 clear D2_temp;
296 D = obj.RHOi_kron*D;
297 obj.D = D;
298 clear D;
299 %=========================================%'
300
301 % Numerical traction operators for BC.
302 %
303 % Formula at boundary j: % tau^{j}_i = sum_l T^{j}_{il} u_l
304 %
305 T_l = cell(dim,1);
306 T_r = cell(dim,1);
307 tau_l = cell(dim,1);
308 tau_r = cell(dim,1);
309
310 D1 = obj.D1;
311
312 d = @kroneckerDelta; % Kronecker delta
313 db = @(i,j) 1-d(i,j); % Logical not of Kronecker delta
314
315 % Boundary j
316 for j = 1:dim
317 T_l{j} = cell(dim,dim);
318 T_r{j} = cell(dim,dim);
319 tau_l{j} = cell(dim,1);
320 tau_r{j} = cell(dim,1);
321
322 [~, n_l] = size(e_l{j});
323 [~, n_r] = size(e_r{j});
324
325 % Traction component i
326 for i = 1:dim
327 tau_l{j}{i} = sparse(dim*m_tot, n_l);
328 tau_r{j}{i} = sparse(dim*m_tot, n_r);
329
330 % Displacement component l
331 for l = 1:dim
332 T_l{j}{i,l} = sparse(m_tot, n_l);
333 T_r{j}{i,l} = sparse(m_tot, n_r);
334
335 % Derivative direction k
336 for k = 1:dim
337 T_l{j}{i,l} = T_l{j}{i,l} ...
338 - d(j,k)*(e_l{j}'*C_mat{j,i,k,l}*e_l{j}*d1_l{k}')'...
339 - db(j,k)*(e_l{j}'*C_mat{j,i,k,l}*D1{k})';
340 T_r{j}{i,l} = T_r{j}{i,l} ...
341 + d(j,k)*(e_r{j}'*C_mat{j,i,k,l}*e_r{j}*d1_r{k}')'...
342 + db(j,k)*(e_r{j}'*C_mat{j,i,k,l}*D1{k})';
343 end
344 tau_l{j}{i} = tau_l{j}{i} + (T_l{j}{i,l}'*E{l}')';
345 tau_r{j}{i} = tau_r{j}{i} + (T_r{j}{i,l}'*E{l}')';
346 end
347 end
348 end
349
350 % Traction tensors, T_ij
351 obj.T_w = T_l{1};
352 obj.T_e = T_r{1};
353 obj.T_s = T_l{2};
354 obj.T_n = T_r{2};
355
356 % Restriction operators
357 obj.e_w = e_w;
358 obj.e_e = e_e;
359 obj.e_s = e_s;
360 obj.e_n = e_n;
361
362 obj.e1_w = e1_w;
363 obj.e1_e = e1_e;
364 obj.e1_s = e1_s;
365 obj.e1_n = e1_n;
366
367 obj.e2_w = e2_w;
368 obj.e2_e = e2_e;
369 obj.e2_s = e2_s;
370 obj.e2_n = e2_n;
371
372 obj.e_scalar_w = e_scalar_w;
373 obj.e_scalar_e = e_scalar_e;
374 obj.e_scalar_s = e_scalar_s;
375 obj.e_scalar_n = e_scalar_n;
376
377
378 obj.e_shifted_scalar_w = e_shifted_scalar_w;
379 obj.e_shifted_scalar_e = e_shifted_scalar_e;
380 obj.e_shifted_scalar_s = e_shifted_scalar_s;
381 obj.e_shifted_scalar_n = e_shifted_scalar_n;
382
383 % First component of traction
384 obj.tau1_w = tau_l{1}{1};
385 obj.tau1_e = tau_r{1}{1};
386 obj.tau1_s = tau_l{2}{1};
387 obj.tau1_n = tau_r{2}{1};
388
389 % Second component of traction
390 obj.tau2_w = tau_l{1}{2};
391 obj.tau2_e = tau_r{1}{2};
392 obj.tau2_s = tau_l{2}{2};
393 obj.tau2_n = tau_r{2}{2};
394
395 % Traction vectors
396 obj.tau_w = (e_w'*e1_w*obj.tau1_w')' + (e_w'*e2_w*obj.tau2_w')';
397 obj.tau_e = (e_e'*e1_e*obj.tau1_e')' + (e_e'*e2_e*obj.tau2_e')';
398 obj.tau_s = (e_s'*e1_s*obj.tau1_s')' + (e_s'*e2_s*obj.tau2_s')';
399 obj.tau_n = (e_n'*e1_n*obj.tau1_n')' + (e_n'*e2_n*obj.tau2_n')';
400
401 % Kroneckered norms and coefficients
402 obj.Hi_kron = kron(obj.Hi, I_dim);
403
404 % Misc.
405 obj.m = m;
406 obj.h = h;
407 obj.order = order;
408 obj.grid = g;
409 obj.dim = dim;
410 obj.nBP = nBP;
411
412 end
413
414
415 % Closure functions return the operators applied to the own domain to close the boundary
416 % Penalty functions return the operators to force the solution. In the case of an interface it returns the operator applied to the other doamin.
417 % boundary is a string specifying the boundary e.g. 'l','r' or 'e','w','n','s'.
418 % bc is a cell array of component and bc type, e.g. {1, 'd'} for Dirichlet condition
419 % on the first component. Can also be e.g.
420 % {'normal', 'd'} or {'tangential', 't'} for conditions on
421 % tangential/normal component.
422 % data is a function returning the data that should be applied at the boundary.
423 % neighbour_scheme is an instance of Scheme that should be interfaced to.
424 % neighbour_boundary is a string specifying which boundary to interface to.
425
426 % For displacement bc:
427 % bc = {comp, 'd', dComps},
428 % where
429 % dComps = vector of components with displacement BC. Default: 1:dim.
430 % In this way, we can specify one BC at a time even though the SATs depend on all BC.
431 function [closure, penalty] = boundary_condition(obj, boundary, bc, tuning)
432 default_arg('tuning', 1.0);
433
434 assert( iscell(bc), 'The BC type must be a 2x1 or 3x1 cell array' );
435 comp = bc{1};
436 type = bc{2};
437 if ischar(comp)
438 comp = obj.getComponent(comp, boundary);
439 end
440
441 e = obj.getBoundaryOperatorForScalarField('e', boundary);
442 e_sh = obj.getBoundaryOperatorForScalarField('e_shifted', boundary);
443 tau = obj.getBoundaryOperator(['tau' num2str(comp)], boundary);
444 T = obj.getBoundaryTractionOperator(boundary);
445 [h11, th_R] = obj.getBorrowing(boundary);
446 H_gamma = obj.getBoundaryQuadratureForScalarField(boundary);
447 nu = obj.getNormal(boundary);
448
449 E = obj.E;
450 Hi = obj.Hi;
451 RHOi = obj.RHOi;
452 C = obj.C;
453
454 dim = obj.dim;
455 m_tot = obj.grid.N();
456
457 % Preallocate
458 [~, col] = size(tau);
459 closure = sparse(dim*m_tot, dim*m_tot);
460 penalty = sparse(dim*m_tot, col);
461
462 j = comp;
463 switch type
464
465 % Dirichlet boundary condition
466 case {'D','d','dirichlet','Dirichlet','displacement','Displacement'}
467
468 if numel(bc) >= 3
469 dComps = bc{3};
470 else
471 dComps = 1:dim;
472 end
473
474 % Loops over components that Dirichlet penalties end up on
475 % Y: symmetrizing part of penalty
476 % Z: symmetric part of penalty
477 % X = Y + Z.
478
479 % Nonsymmetric part goes on all components to
480 % yield traction in discrete energy rate
481 for i = 1:dim
482 Y = T{j,i}';
483 X = e*Y;
484 closure = closure + E{i}*RHOi*Hi*X'*e*H_gamma*(e'*E{j}' );
485 penalty = penalty - E{i}*RHOi*Hi*X'*e*H_gamma;
486 end
487
488 % Symmetric part only required on components with displacement BC.
489 % (Otherwise it's not symmetric.)
490
491 % 1. Compute beta such that C_bJbL_boundary - \beta*C_bJbL_i >= 0, i = 0, 1, ..., nBP.
492 [~, N] = size(e);
493 beta_vec = ones(N, 1);
494 b = obj.getComponent('normal', boundary);
495 C_boundary = cell(dim, dim, dim, dim);
496 for i = 1:dim
497 for j = 1:dim
498 for k = 1:dim
499 for l = 1:dim
500 C_boundary{i,j,k,l} = e'*C{i,j,k,l}*e ;
501 end
502 end
503 end
504 end
505
506 % Loop over shift inward from boundary
507 for m = 1:obj.nBP-1
508 C_shifted = cell(dim, dim, dim, dim);
509 for i = 1:dim
510 for j = 1:dim
511 for k = 1:dim
512 for l = 1:dim
513 C_shifted{i,j,k,l} = e_sh{m}'*C{i,j,k,l}*e_sh{m};
514 end
515 end
516 end
517 end
518
519 % Loop along boundary
520 for i = 1:N
521 C_boundary_mat = [C_boundary{b,1,b,1}(i,i), C_boundary{b,1,b,2}(i,i); ...
522 C_boundary{b,2,b,1}(i,i), C_boundary{b,2,b,2}(i,i)];
523
524 C_shifted_mat = [C_shifted{b,1,b,1}(i,i), C_shifted{b,1,b,2}(i,i); ...
525 C_shifted{b,2,b,1}(i,i), C_shifted{b,2,b,2}(i,i)];
526
527 beta = obj.computeBorrowingFromC(C_boundary_mat, C_shifted_mat);
528 beta_vec(i) = min(beta_vec(i), beta);
529 end
530 end
531 if min(beta_vec) < 1e-10
532 disp('WARNING! No borrowing from C seems to be possible.')
533 disp(['min(beta) = ' num2str(min(beta_vec))])
534 end
535 beta_inv = e*spdiag(1./beta_vec)*e';
536
537 % Compensate at corners
538 corner_weight = ones(N, 1);
539 corner_weight(1) = 2;
540 corner_weight(end) = 2;
541 corner_weight = e*spdiag(corner_weight)*e';
542
543
544 % 2. Build Z.
545 j = comp;
546 for i = dComps
547 Z = sparse(m_tot, m_tot);
548 for l = 1:dim
549 for k = 1:dim
550 Z = Z + nu(l)*C{l,i,k,j}*nu(k);
551 end
552 end
553 Z = -1.2*tuning*(corner_weight/h11 + beta_inv/th_R)*Z;
554 X = Z;
555 closure = closure + E{i}*RHOi*Hi*X'*e*H_gamma*(e'*E{j}' );
556 penalty = penalty - E{i}*RHOi*Hi*X'*e*H_gamma;
557 end
558
559 % Free boundary condition
560 case {'F','f','Free','free','traction','Traction','t','T'}
561 closure = closure - E{j}*RHOi*Hi*e*H_gamma*tau';
562 penalty = penalty + E{j}*RHOi*Hi*e*H_gamma;
563
564 % Unknown boundary condition
565 otherwise
566 error('No such boundary condition: type = %s',type);
567 end
568 end
569
570 % type Struct that specifies the interface coupling.
571 % Fields:
572 % -- tuning: penalty strength, defaults to 1.0
573 % -- interpolation: type of interpolation, default 'none'
574 function [closure, penalty] = interface(obj,boundary,neighbour_scheme,neighbour_boundary,type)
575
576 defaultType.tuning = 1.0;
577 defaultType.interpolation = 'none';
578 default_struct('type', defaultType);
579
580 switch type.interpolation
581 case {'none', ''}
582 [closure, penalty] = interfaceStandard(obj,boundary,neighbour_scheme,neighbour_boundary,type);
583 case {'op','OP'}
584 [closure, penalty] = interfaceNonConforming(obj,boundary,neighbour_scheme,neighbour_boundary,type);
585 otherwise
586 error('Unknown type of interpolation: %s ', type.interpolation);
587 end
588 end
589
590 function [closure, penalty] = interfaceStandard(obj,boundary,neighbour_scheme,neighbour_boundary,type)
591 tuning = type.tuning;
592
593 % u denotes the solution in the own domain
594 % v denotes the solution in the neighbour domain
595
596 u = obj;
597 v = neighbour_scheme;
598
599 % Operators, u side
600 e_u = u.getBoundaryOperatorForScalarField('e', boundary);
601 tau_u = u.getBoundaryOperator('tau', boundary);
602 h11_u = u.getBorrowing(boundary);
603 nu_u = u.getNormal(boundary);
604
605 E_u = u.E;
606 C_u = u.C;
607 m_tot_u = u.grid.N();
608
609 % Operators, v side
610 e_v = v.getBoundaryOperatorForScalarField('e', neighbour_boundary);
611 tau_v = v.getBoundaryOperator('tau', neighbour_boundary);
612 h11_v = v.getBorrowing(neighbour_boundary);
613 nu_v = v.getNormal(neighbour_boundary);
614
615 E_v = v.E;
616 C_v = v.C;
617 m_tot_v = v.grid.N();
618
619 % Fix {'e', 's'}, {'w', 'n'}, and {'x','x'} couplings
620 flipFlag = false;
621 e_v_flip = e_v;
622 if (strcmp(boundary,'s') && strcmp(neighbour_boundary,'e')) || ...
623 (strcmp(boundary,'e') && strcmp(neighbour_boundary,'s')) || ...
624 (strcmp(boundary,'w') && strcmp(neighbour_boundary,'n')) || ...
625 (strcmp(boundary,'n') && strcmp(neighbour_boundary,'w')) || ...
626 (strcmp(boundary,'s') && strcmp(neighbour_boundary,'s')) || ...
627 (strcmp(boundary,'n') && strcmp(neighbour_boundary,'n')) || ...
628 (strcmp(boundary,'w') && strcmp(neighbour_boundary,'w')) || ...
629 (strcmp(boundary,'e') && strcmp(neighbour_boundary,'e'))
630
631 flipFlag = true;
632 e_v_flip = fliplr(e_v);
633
634 t1 = tau_v(:,1:2:end-1);
635 t2 = tau_v(:,2:2:end);
636
637 t1 = fliplr(t1);
638 t2 = fliplr(t2);
639
640 tau_v(:,1:2:end-1) = t1;
641 tau_v(:,2:2:end) = t2;
642 end
643
644 % Operators that are only required for own domain
645 Hi = u.Hi_kron;
646 RHOi = u.RHOi_kron;
647 e_kron = u.getBoundaryOperator('e', boundary);
648 T_u = u.getBoundaryTractionOperator(boundary);
649
650 % Shared operators
651 H_gamma = u.getBoundaryQuadratureForScalarField(boundary);
652 H_gamma_kron = u.getBoundaryQuadrature(boundary);
653 dim = u.dim;
654
655 % Preallocate
656 [~, m_int] = size(H_gamma);
657 closure = sparse(dim*m_tot_u, dim*m_tot_u);
658 penalty = sparse(dim*m_tot_u, dim*m_tot_v);
659
660 % ---- Continuity of displacement ------
661
662 % Y: symmetrizing part of penalty
663 % Z: symmetric part of penalty
664 % X = Y + Z.
665
666 % Loop over components to couple across interface
667 for j = 1:dim
668
669 % Loop over components that penalties end up on
670 for i = 1:dim
671 Y = 1/2*T_u{j,i}';
672 Z_u = sparse(m_int, m_int);
673 Z_v = sparse(m_int, m_int);
674 for l = 1:dim
675 for k = 1:dim
676 Z_u = Z_u + e_u'*nu_u(l)*C_u{l,i,k,j}*nu_u(k)*e_u;
677 Z_v = Z_v + e_v'*nu_v(l)*C_v{l,i,k,j}*nu_v(k)*e_v;
678 end
679 end
680
681 if flipFlag
682 Z_v = rot90(Z_v,2);
683 end
684
685 Z = -tuning*dim*( 1/(4*h11_u)*Z_u + 1/(4*h11_v)*Z_v );
686 X = Y + Z*e_u';
687 closure = closure + E_u{i}*X'*H_gamma*e_u'*E_u{j}';
688 penalty = penalty - E_u{i}*X'*H_gamma*e_v_flip'*E_v{j}';
689
690 end
691 end
692
693 % ---- Continuity of traction ------
694 closure = closure - 1/2*e_kron*H_gamma_kron*tau_u';
695 penalty = penalty - 1/2*e_kron*H_gamma_kron*tau_v';
696
697 % ---- Multiply by inverse of density x quadraure ----
698 closure = RHOi*Hi*closure;
699 penalty = RHOi*Hi*penalty;
700
701 end
702
703 function [closure, penalty] = interfaceNonConforming(obj,boundary,neighbour_scheme,neighbour_boundary,type)
704 error('Non-conforming interfaces not implemented yet.');
705 end
706
707 % Computes the largest beta such that C_b - beta*C_s >= 0, using bisection.
708 function beta = computeBorrowingFromC(obj, C_b, C_s)
709 tol = 1e-8;
710 beta_min = 0;
711 beta_max = 1;
712 beta = 0.5;
713 err = (beta_max - beta_min)/2;
714 while err > tol
715 if min(eig(C_b - beta*C_s)) < -1e-14
716 % Tried to borrow too much. Reduce beta.
717 beta_max = beta;
718 beta = (beta_min + beta)/2;
719 else
720 % Increase beta
721 beta_min = beta;
722 beta = (beta_max + beta)/2;
723 end
724 err = err/2;
725 end
726 end
727
728 % Returns the component number that is the tangential/normal component
729 % at the specified boundary
730 function comp = getComponent(obj, comp_str, boundary)
731 assertIsMember(comp_str, {'normal', 'tangential'});
732 assertIsMember(boundary, {'w', 'e', 's', 'n'});
733
734 switch boundary
735 case {'w', 'e'}
736 switch comp_str
737 case 'normal'
738 comp = 1;
739 case 'tangential'
740 comp = 2;
741 end
742 case {'s', 'n'}
743 switch comp_str
744 case 'normal'
745 comp = 2;
746 case 'tangential'
747 comp = 1;
748 end
749 end
750 end
751
752 % Returns h11 for the boundary specified by the string boundary.
753 % op -- string
754 function [h11, theta_R] = getBorrowing(obj, boundary)
755 assertIsMember(boundary, {'w', 'e', 's', 'n'})
756
757 switch boundary
758 case {'w','e'}
759 h11 = obj.h11{1};
760 theta_R = obj.theta_R{1};
761 case {'s', 'n'}
762 h11 = obj.h11{2};
763 theta_R = obj.theta_R{2};
764 end
765 end
766
767 % Returns the outward unit normal vector for the boundary specified by the string boundary.
768 function nu = getNormal(obj, boundary)
769 assertIsMember(boundary, {'w', 'e', 's', 'n'})
770
771 switch boundary
772 case 'w'
773 nu = [-1,0];
774 case 'e'
775 nu = [1,0];
776 case 's'
777 nu = [0,-1];
778 case 'n'
779 nu = [0,1];
780 end
781 end
782
783 % Returns the boundary operator op for the boundary specified by the string boundary.
784 % op -- string
785 function o = getBoundaryOperator(obj, op, boundary)
786 assertIsMember(boundary, {'w', 'e', 's', 'n'})
787 assertIsMember(op, {'e', 'e1', 'e2', 'tau', 'tau1', 'tau2'})
788
789 switch op
790 case {'e', 'e1', 'e2', 'tau', 'tau1', 'tau2'}
791 o = obj.([op, '_', boundary]);
792 end
793
794 end
795
796 % Returns the boundary operator op for the boundary specified by the string boundary.
797 % op -- string
798 function o = getBoundaryOperatorForScalarField(obj, op, boundary)
799 assertIsMember(boundary, {'w', 'e', 's', 'n'})
800 assertIsMember(op, {'e', 'e_shifted'})
801
802 switch op
803
804 case 'e'
805 o = obj.(['e_scalar', '_', boundary]);
806 case 'e_shifted'
807 o = obj.(['e_shifted_scalar', '_', boundary]);
808 end
809
810 end
811
812 % Returns the boundary operator T_ij (cell format) for the boundary specified by the string boundary.
813 % Formula: tau_i = T_ij u_j
814 % op -- string
815 function T = getBoundaryTractionOperator(obj, boundary)
816 assertIsMember(boundary, {'w', 'e', 's', 'n'})
817
818 T = obj.(['T', '_', boundary]);
819 end
820
821 % Returns square boundary quadrature matrix, of dimension
822 % corresponding to the number of boundary unknowns
823 %
824 % boundary -- string
825 function H = getBoundaryQuadrature(obj, boundary)
826 assertIsMember(boundary, {'w', 'e', 's', 'n'})
827
828 H = obj.getBoundaryQuadratureForScalarField(boundary);
829 I_dim = speye(obj.dim, obj.dim);
830 H = kron(H, I_dim);
831 end
832
833 % Returns square boundary quadrature matrix, of dimension
834 % corresponding to the number of boundary grid points
835 %
836 % boundary -- string
837 function H_b = getBoundaryQuadratureForScalarField(obj, boundary)
838 assertIsMember(boundary, {'w', 'e', 's', 'n'})
839
840 H_b = obj.(['H_', boundary]);
841 end
842
843 function N = size(obj)
844 N = obj.dim*prod(obj.m);
845 end
846 end
847 end