comparison +scheme/Elastic2dCurvilinearAnisotropicUpwind.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 8ff3a95ad7cc
children
comparison
equal deleted inserted replaced
1330:855871e0b852 1331:60c875c18de3
1 classdef Elastic2dCurvilinearAnisotropicUpwind < scheme.Scheme
2
3 % Discretizes the elastic wave equation:
4 % rho u_{i,tt} = dj C_{ijkl} dk u_j
5 % in curvilinear coordinates.
6 % opSet should be cell array of opSets, one per dimension. This
7 % is useful if we have periodic BC in one direction.
8 % Assumes fully compatible operators.
9
10 properties
11 m % Number of points in each direction, possibly a vector
12 h % Grid spacing
13
14 grid
15 dim
16
17 order % Order of accuracy for the approximation
18
19 % Diagonal matrices for variable coefficients
20 J, Ji
21 RHO % Density
22 C % Elastic stiffness tensor
23
24 D % Total operator
25
26 Dx, Dy % Physical derivatives
27 sigma % Cell matrix of physical stress operators
28 n_w, n_e, n_s, n_n % Physical normals
29
30 % Boundary operators in cell format, used for BC
31 T_w, T_e, T_s, T_n
32
33 % Traction operators
34 tau_w, tau_e, tau_s, tau_n % Return vector field
35 tau1_w, tau1_e, tau1_s, tau1_n % Return scalar field
36 tau2_w, tau2_e, tau2_s, tau2_n % Return scalar field
37
38 % Inner products
39 H
40
41 % Boundary inner products (for scalar field)
42 H_w, H_e, H_s, H_n
43
44 % Surface Jacobian vectors
45 s_w, s_e, s_s, s_n
46
47 % Boundary restriction operators
48 e_w, e_e, e_s, e_n % Act on vector field, return vector field at boundary
49 e1_w, e1_e, e1_s, e1_n % Act on vector field, return scalar field at boundary
50 e2_w, e2_e, e2_s, e2_n % Act on vector field, return scalar field at boundary
51 e_scalar_w, e_scalar_e, e_scalar_s, e_scalar_n; % Act on scalar field, return scalar field
52 en_w, en_e, en_s, en_n % Act on vector field, return normal component
53
54 % E{i}^T picks out component i
55 E
56
57 % Elastic2dVariableAnisotropic object for reference domain
58 refObj
59 end
60
61 methods
62
63 % The coefficients can either be function handles or grid functions
64 % optFlag -- if true, extra computations are performed, which may be helpful for optimization.
65 function obj = Elastic2dCurvilinearAnisotropicUpwind(g, order, rho, C, opSet, optFlag)
66 default_arg('rho', @(x,y) 0*x+1);
67 default_arg('opSet',{@sbp.D1Upwind, @sbp.D1Upwind});
68 default_arg('optFlag', false);
69 dim = 2;
70
71 C_default = cell(dim,dim,dim,dim);
72 for i = 1:dim
73 for j = 1:dim
74 for k = 1:dim
75 for l = 1:dim
76 C_default{i,j,k,l} = @(x,y) 0*x;
77 end
78 end
79 end
80 end
81 default_arg('C', C_default);
82
83 assert(isa(g, 'grid.Curvilinear'));
84
85 if isa(rho, 'function_handle')
86 rho = grid.evalOn(g, rho);
87 end
88
89 C_mat = cell(dim,dim,dim,dim);
90 for i = 1:dim
91 for j = 1:dim
92 for k = 1:dim
93 for l = 1:dim
94 if isa(C{i,j,k,l}, 'function_handle')
95 C{i,j,k,l} = grid.evalOn(g, C{i,j,k,l});
96 end
97 C_mat{i,j,k,l} = spdiag(C{i,j,k,l});
98 end
99 end
100 end
101 end
102 obj.C = C_mat;
103
104 m = g.size();
105 m_tot = g.N();
106
107 % 1D operators
108 opSetMetric = {@sbp.D2VariableCompatible, @sbp.D2VariableCompatible};
109 orderMetric = ceil(order/2)*2;
110 m_u = m(1);
111 m_v = m(2);
112 ops_u = opSetMetric{1}(m_u, {0, 1}, orderMetric);
113 ops_v = opSetMetric{2}(m_v, {0, 1}, orderMetric);
114
115 h_u = ops_u.h;
116 h_v = ops_v.h;
117
118 I_u = speye(m_u);
119 I_v = speye(m_v);
120
121 D1_u = ops_u.D1;
122 H_u = ops_u.H;
123 Hi_u = ops_u.HI;
124 e_l_u = ops_u.e_l;
125 e_r_u = ops_u.e_r;
126 d1_l_u = ops_u.d1_l;
127 d1_r_u = ops_u.d1_r;
128
129 D1_v = ops_v.D1;
130 H_v = ops_v.H;
131 Hi_v = ops_v.HI;
132 e_l_v = ops_v.e_l;
133 e_r_v = ops_v.e_r;
134 d1_l_v = ops_v.d1_l;
135 d1_r_v = ops_v.d1_r;
136
137
138 % Logical operators
139 Du = kr(D1_u,I_v);
140 Dv = kr(I_u,D1_v);
141
142 e_w = kr(e_l_u,I_v);
143 e_e = kr(e_r_u,I_v);
144 e_s = kr(I_u,e_l_v);
145 e_n = kr(I_u,e_r_v);
146
147 % Metric coefficients
148 coords = g.points();
149 x = coords(:,1);
150 y = coords(:,2);
151
152 x_u = Du*x;
153 x_v = Dv*x;
154 y_u = Du*y;
155 y_v = Dv*y;
156
157 J = x_u.*y_v - x_v.*y_u;
158
159 K = cell(dim, dim);
160 K{1,1} = y_v./J;
161 K{1,2} = -y_u./J;
162 K{2,1} = -x_v./J;
163 K{2,2} = x_u./J;
164
165 % Physical derivatives
166 obj.Dx = spdiag( y_v./J)*Du + spdiag(-y_u./J)*Dv;
167 obj.Dy = spdiag(-x_v./J)*Du + spdiag( x_u./J)*Dv;
168
169 % Wrap around Aniosotropic Cartesian
170 rho_tilde = J.*rho;
171
172 PHI = cell(dim,dim,dim,dim);
173 for i = 1:dim
174 for j = 1:dim
175 for k = 1:dim
176 for l = 1:dim
177 PHI{i,j,k,l} = 0*C{i,j,k,l};
178 for m = 1:dim
179 for n = 1:dim
180 PHI{i,j,k,l} = PHI{i,j,k,l} + J.*K{m,i}.*C{m,j,n,l}.*K{n,k};
181 end
182 end
183 end
184 end
185 end
186 end
187
188 gRef = grid.equidistant([m_u, m_v], {0,1}, {0,1});
189 refObj = scheme.Elastic2dVariableAnisotropicUpwind(gRef, order, rho_tilde, PHI, opSet);
190
191 %---- Set object properties ------
192 obj.RHO = spdiag(rho);
193
194 % Volume quadrature
195 obj.J = spdiag(J);
196 obj.Ji = spdiag(1./J);
197 obj.H = obj.J*refObj.H;
198
199 % Boundary quadratures
200 s_w = sqrt((e_w'*x_v).^2 + (e_w'*y_v).^2);
201 s_e = sqrt((e_e'*x_v).^2 + (e_e'*y_v).^2);
202 s_s = sqrt((e_s'*x_u).^2 + (e_s'*y_u).^2);
203 s_n = sqrt((e_n'*x_u).^2 + (e_n'*y_u).^2);
204 obj.s_w = s_w;
205 obj.s_e = s_e;
206 obj.s_s = s_s;
207 obj.s_n = s_n;
208
209 obj.H_w = H_v*spdiag(s_w);
210 obj.H_e = H_v*spdiag(s_e);
211 obj.H_s = H_u*spdiag(s_s);
212 obj.H_n = H_u*spdiag(s_n);
213
214 % Restriction operators
215 obj.e_w = refObj.e_w;
216 obj.e_e = refObj.e_e;
217 obj.e_s = refObj.e_s;
218 obj.e_n = refObj.e_n;
219
220 % Adapt things from reference object
221 obj.D = refObj.D;
222 obj.E = refObj.E;
223
224 obj.e1_w = refObj.e1_w;
225 obj.e1_e = refObj.e1_e;
226 obj.e1_s = refObj.e1_s;
227 obj.e1_n = refObj.e1_n;
228
229 obj.e2_w = refObj.e2_w;
230 obj.e2_e = refObj.e2_e;
231 obj.e2_s = refObj.e2_s;
232 obj.e2_n = refObj.e2_n;
233
234 obj.e_scalar_w = refObj.e_scalar_w;
235 obj.e_scalar_e = refObj.e_scalar_e;
236 obj.e_scalar_s = refObj.e_scalar_s;
237 obj.e_scalar_n = refObj.e_scalar_n;
238
239 e1_w = obj.e1_w;
240 e1_e = obj.e1_e;
241 e1_s = obj.e1_s;
242 e1_n = obj.e1_n;
243
244 e2_w = obj.e2_w;
245 e2_e = obj.e2_e;
246 e2_s = obj.e2_s;
247 e2_n = obj.e2_n;
248
249 obj.tau1_w = (spdiag(1./s_w)*refObj.tau1_w')';
250 obj.tau1_e = (spdiag(1./s_e)*refObj.tau1_e')';
251 obj.tau1_s = (spdiag(1./s_s)*refObj.tau1_s')';
252 obj.tau1_n = (spdiag(1./s_n)*refObj.tau1_n')';
253
254 obj.tau2_w = (spdiag(1./s_w)*refObj.tau2_w')';
255 obj.tau2_e = (spdiag(1./s_e)*refObj.tau2_e')';
256 obj.tau2_s = (spdiag(1./s_s)*refObj.tau2_s')';
257 obj.tau2_n = (spdiag(1./s_n)*refObj.tau2_n')';
258
259 obj.tau_w = (refObj.e_w'*obj.e1_w*obj.tau1_w')' + (refObj.e_w'*obj.e2_w*obj.tau2_w')';
260 obj.tau_e = (refObj.e_e'*obj.e1_e*obj.tau1_e')' + (refObj.e_e'*obj.e2_e*obj.tau2_e')';
261 obj.tau_s = (refObj.e_s'*obj.e1_s*obj.tau1_s')' + (refObj.e_s'*obj.e2_s*obj.tau2_s')';
262 obj.tau_n = (refObj.e_n'*obj.e1_n*obj.tau1_n')' + (refObj.e_n'*obj.e2_n*obj.tau2_n')';
263
264 % Physical normals
265 e_w = obj.e_scalar_w;
266 e_e = obj.e_scalar_e;
267 e_s = obj.e_scalar_s;
268 e_n = obj.e_scalar_n;
269
270 e_w_vec = obj.e_w;
271 e_e_vec = obj.e_e;
272 e_s_vec = obj.e_s;
273 e_n_vec = obj.e_n;
274
275 nu_w = [-1,0];
276 nu_e = [1,0];
277 nu_s = [0,-1];
278 nu_n = [0,1];
279
280 obj.n_w = cell(2,1);
281 obj.n_e = cell(2,1);
282 obj.n_s = cell(2,1);
283 obj.n_n = cell(2,1);
284
285 n_w_1 = (1./s_w).*e_w'*(J.*(K{1,1}*nu_w(1) + K{1,2}*nu_w(2)));
286 n_w_2 = (1./s_w).*e_w'*(J.*(K{2,1}*nu_w(1) + K{2,2}*nu_w(2)));
287 obj.n_w{1} = spdiag(n_w_1);
288 obj.n_w{2} = spdiag(n_w_2);
289
290 n_e_1 = (1./s_e).*e_e'*(J.*(K{1,1}*nu_e(1) + K{1,2}*nu_e(2)));
291 n_e_2 = (1./s_e).*e_e'*(J.*(K{2,1}*nu_e(1) + K{2,2}*nu_e(2)));
292 obj.n_e{1} = spdiag(n_e_1);
293 obj.n_e{2} = spdiag(n_e_2);
294
295 n_s_1 = (1./s_s).*e_s'*(J.*(K{1,1}*nu_s(1) + K{1,2}*nu_s(2)));
296 n_s_2 = (1./s_s).*e_s'*(J.*(K{2,1}*nu_s(1) + K{2,2}*nu_s(2)));
297 obj.n_s{1} = spdiag(n_s_1);
298 obj.n_s{2} = spdiag(n_s_2);
299
300 n_n_1 = (1./s_n).*e_n'*(J.*(K{1,1}*nu_n(1) + K{1,2}*nu_n(2)));
301 n_n_2 = (1./s_n).*e_n'*(J.*(K{2,1}*nu_n(1) + K{2,2}*nu_n(2)));
302 obj.n_n{1} = spdiag(n_n_1);
303 obj.n_n{2} = spdiag(n_n_2);
304
305 % Operators that extract the normal component
306 obj.en_w = (obj.n_w{1}*obj.e1_w' + obj.n_w{2}*obj.e2_w')';
307 obj.en_e = (obj.n_e{1}*obj.e1_e' + obj.n_e{2}*obj.e2_e')';
308 obj.en_s = (obj.n_s{1}*obj.e1_s' + obj.n_s{2}*obj.e2_s')';
309 obj.en_n = (obj.n_n{1}*obj.e1_n' + obj.n_n{2}*obj.e2_n')';
310
311 % Stress operators
312 sigma = cell(dim, dim);
313 D1 = {obj.Dx, obj.Dy};
314 E = obj.E;
315 N = length(obj.RHO);
316 for i = 1:dim
317 for j = 1:dim
318 sigma{i,j} = sparse(N,2*N);
319 for k = 1:dim
320 for l = 1:dim
321 sigma{i,j} = sigma{i,j} + obj.C{i,j,k,l}*D1{k}*E{l}';
322 end
323 end
324 end
325 end
326 obj.sigma = sigma;
327
328 % Misc.
329 obj.refObj = refObj;
330 obj.m = refObj.m;
331 obj.h = refObj.h;
332 obj.order = order;
333 obj.grid = g;
334 obj.dim = dim;
335
336 end
337
338
339 % Closure functions return the operators applied to the own domain to close the boundary
340 % Penalty functions return the operators to force the solution. In the case of an interface it returns the operator applied to the other doamin.
341 % boundary is a string specifying the boundary e.g. 'l','r' or 'e','w','n','s'.
342 % bc is a cell array of component and bc type, e.g. {1, 'd'} for Dirichlet condition
343 % on the first component. Can also be e.g.
344 % {'normal', 'd'} or {'tangential', 't'} for conditions on
345 % tangential/normal component.
346 % data is a function returning the data that should be applied at the boundary.
347 % neighbour_scheme is an instance of Scheme that should be interfaced to.
348 % neighbour_boundary is a string specifying which boundary to interface to.
349
350 % For displacement bc:
351 % bc = {comp, 'd', dComps},
352 % where
353 % dComps = vector of components with displacement BC. Default: 1:dim.
354 % In this way, we can specify one BC at a time even though the SATs depend on all BC.
355 function [closure, penalty] = boundary_condition(obj, boundary, bc, tuning)
356 default_arg('tuning', 1.0);
357 assert( iscell(bc), 'The BC type must be a 2x1 or 3x1 cell array' );
358
359 [closure, penalty] = obj.refObj.boundary_condition(boundary, bc, tuning);
360
361 type = bc{2};
362
363 switch type
364 case {'F','f','Free','free','traction','Traction','t','T'}
365 s = obj.(['s_' boundary]);
366 s = spdiag(s);
367 penalty = penalty*s;
368 end
369 end
370
371 % type Struct that specifies the interface coupling.
372 % Fields:
373 % -- tuning: penalty strength, defaults to 1.0
374 % -- interpolation: type of interpolation, default 'none'
375 function [closure, penalty] = interface(obj,boundary,neighbour_scheme,neighbour_boundary,type)
376
377 defaultType.tuning = 1.0;
378 defaultType.interpolation = 'none';
379 default_struct('type', defaultType);
380
381 [closure, penalty] = obj.refObj.interface(boundary,neighbour_scheme.refObj,neighbour_boundary,type);
382 end
383
384 % Returns h11 for the boundary specified by the string boundary.
385 % op -- string
386 function h11 = getBorrowing(obj, boundary)
387 assertIsMember(boundary, {'w', 'e', 's', 'n'})
388
389 switch boundary
390 case {'w','e'}
391 h11 = obj.refObj.h11{1};
392 case {'s', 'n'}
393 h11 = obj.refObj.h11{2};
394 end
395 end
396
397 % Returns the outward unit normal vector for the boundary specified by the string boundary.
398 % n is a cell of diagonal matrices for each normal component, n{1} = n_1, n{2} = n_2.
399 function n = getNormal(obj, boundary)
400 assertIsMember(boundary, {'w', 'e', 's', 'n'})
401
402 n = obj.(['n_' boundary]);
403 end
404
405 % Returns the boundary operator op for the boundary specified by the string boundary.
406 % op -- string
407 function o = getBoundaryOperator(obj, op, boundary)
408 assertIsMember(boundary, {'w', 'e', 's', 'n'})
409 assertIsMember(op, {'e', 'e1', 'e2', 'tau', 'tau1', 'tau2', 'en'})
410
411 o = obj.([op, '_', boundary]);
412
413 end
414
415 % Returns the boundary operator op for the boundary specified by the string boundary.
416 % op -- string
417 function o = getBoundaryOperatorForScalarField(obj, op, boundary)
418 assertIsMember(boundary, {'w', 'e', 's', 'n'})
419 assertIsMember(op, {'e'})
420
421 switch op
422
423 case 'e'
424 o = obj.(['e_scalar', '_', boundary]);
425 end
426
427 end
428
429 % Returns the boundary operator T_ij (cell format) for the boundary specified by the string boundary.
430 % Formula: tau_i = T_ij u_j
431 % op -- string
432 function T = getBoundaryTractionOperator(obj, boundary)
433 assertIsMember(boundary, {'w', 'e', 's', 'n'})
434
435 T = obj.(['T', '_', boundary]);
436 end
437
438 % Returns square boundary quadrature matrix, of dimension
439 % corresponding to the number of boundary unknowns
440 %
441 % boundary -- string
442 function H = getBoundaryQuadrature(obj, boundary)
443 assertIsMember(boundary, {'w', 'e', 's', 'n'})
444
445 H = obj.getBoundaryQuadratureForScalarField(boundary);
446 I_dim = speye(obj.dim, obj.dim);
447 H = kron(H, I_dim);
448 end
449
450 % Returns square boundary quadrature matrix, of dimension
451 % corresponding to the number of boundary grid points
452 %
453 % boundary -- string
454 function H_b = getBoundaryQuadratureForScalarField(obj, boundary)
455 assertIsMember(boundary, {'w', 'e', 's', 'n'})
456
457 H_b = obj.(['H_', boundary]);
458 end
459
460 function N = size(obj)
461 N = obj.dim*prod(obj.m);
462 end
463 end
464 end