comparison +scheme/Elastic2dCurvilinearAnisotropicUpwind.m @ 1227:02dfe3a56742 feature/poroelastic

Add Upwind ElasticAnisotropic schemes. Seem to work really well!
author Martin Almquist <malmquist@stanford.edu>
date Sat, 16 Nov 2019 14:26:06 -0800
parents
children 9a7cac4202d0
comparison
equal deleted inserted replaced
1214:e1d4cb8b5309 1227:02dfe3a56742
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.D2VariableCompatible, @sbp.D2VariableCompatible});
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 + 1;
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 m_u = m(1);
109 m_v = m(2);
110 ops_u = opSet{1}(m_u, {0, 1}, order);
111 ops_v = opSet{2}(m_v, {0, 1}, order);
112
113 h_u = ops_u.h;
114 h_v = ops_v.h;
115
116 I_u = speye(m_u);
117 I_v = speye(m_v);
118
119 D1_u = ops_u.D1;
120 H_u = ops_u.H;
121 Hi_u = ops_u.HI;
122 e_l_u = ops_u.e_l;
123 e_r_u = ops_u.e_r;
124 d1_l_u = ops_u.d1_l;
125 d1_r_u = ops_u.d1_r;
126
127 D1_v = ops_v.D1;
128 H_v = ops_v.H;
129 Hi_v = ops_v.HI;
130 e_l_v = ops_v.e_l;
131 e_r_v = ops_v.e_r;
132 d1_l_v = ops_v.d1_l;
133 d1_r_v = ops_v.d1_r;
134
135
136 % Logical operators
137 Du = kr(D1_u,I_v);
138 Dv = kr(I_u,D1_v);
139
140 e_w = kr(e_l_u,I_v);
141 e_e = kr(e_r_u,I_v);
142 e_s = kr(I_u,e_l_v);
143 e_n = kr(I_u,e_r_v);
144
145 % Metric coefficients
146 coords = g.points();
147 x = coords(:,1);
148 y = coords(:,2);
149
150 x_u = Du*x;
151 x_v = Dv*x;
152 y_u = Du*y;
153 y_v = Dv*y;
154
155 J = x_u.*y_v - x_v.*y_u;
156
157 K = cell(dim, dim);
158 K{1,1} = y_v./J;
159 K{1,2} = -y_u./J;
160 K{2,1} = -x_v./J;
161 K{2,2} = x_u./J;
162
163 % Physical derivatives
164 obj.Dx = spdiag( y_v./J)*Du + spdiag(-y_u./J)*Dv;
165 obj.Dy = spdiag(-x_v./J)*Du + spdiag( x_u./J)*Dv;
166
167 % Wrap around Aniosotropic Cartesian
168 rho_tilde = J.*rho;
169
170 PHI = cell(dim,dim,dim,dim);
171 for i = 1:dim
172 for j = 1:dim
173 for k = 1:dim
174 for l = 1:dim
175 PHI{i,j,k,l} = 0*C{i,j,k,l};
176 for m = 1:dim
177 for n = 1:dim
178 PHI{i,j,k,l} = PHI{i,j,k,l} + J.*K{m,i}.*C{m,j,n,l}.*K{n,k};
179 end
180 end
181 end
182 end
183 end
184 end
185
186 gRef = grid.equidistant([m_u, m_v], {0,1}, {0,1});
187 refObj = scheme.Elastic2dVariableAnisotropicUpwind(gRef, order, rho_tilde, PHI, []);
188
189 %---- Set object properties ------
190 obj.RHO = spdiag(rho);
191
192 % Volume quadrature
193 obj.J = spdiag(J);
194 obj.Ji = spdiag(1./J);
195 obj.H = obj.J*kr(H_u,H_v);
196
197 % Boundary quadratures
198 s_w = sqrt((e_w'*x_v).^2 + (e_w'*y_v).^2);
199 s_e = sqrt((e_e'*x_v).^2 + (e_e'*y_v).^2);
200 s_s = sqrt((e_s'*x_u).^2 + (e_s'*y_u).^2);
201 s_n = sqrt((e_n'*x_u).^2 + (e_n'*y_u).^2);
202 obj.s_w = s_w;
203 obj.s_e = s_e;
204 obj.s_s = s_s;
205 obj.s_n = s_n;
206
207 obj.H_w = H_v*spdiag(s_w);
208 obj.H_e = H_v*spdiag(s_e);
209 obj.H_s = H_u*spdiag(s_s);
210 obj.H_n = H_u*spdiag(s_n);
211
212 % Restriction operators
213 obj.e_w = refObj.e_w;
214 obj.e_e = refObj.e_e;
215 obj.e_s = refObj.e_s;
216 obj.e_n = refObj.e_n;
217
218 % Adapt things from reference object
219 obj.D = refObj.D;
220 obj.E = refObj.E;
221
222 obj.e1_w = refObj.e1_w;
223 obj.e1_e = refObj.e1_e;
224 obj.e1_s = refObj.e1_s;
225 obj.e1_n = refObj.e1_n;
226
227 obj.e2_w = refObj.e2_w;
228 obj.e2_e = refObj.e2_e;
229 obj.e2_s = refObj.e2_s;
230 obj.e2_n = refObj.e2_n;
231
232 obj.e_scalar_w = refObj.e_scalar_w;
233 obj.e_scalar_e = refObj.e_scalar_e;
234 obj.e_scalar_s = refObj.e_scalar_s;
235 obj.e_scalar_n = refObj.e_scalar_n;
236
237 e1_w = obj.e1_w;
238 e1_e = obj.e1_e;
239 e1_s = obj.e1_s;
240 e1_n = obj.e1_n;
241
242 e2_w = obj.e2_w;
243 e2_e = obj.e2_e;
244 e2_s = obj.e2_s;
245 e2_n = obj.e2_n;
246
247 obj.tau1_w = (spdiag(1./s_w)*refObj.tau1_w')';
248 obj.tau1_e = (spdiag(1./s_e)*refObj.tau1_e')';
249 obj.tau1_s = (spdiag(1./s_s)*refObj.tau1_s')';
250 obj.tau1_n = (spdiag(1./s_n)*refObj.tau1_n')';
251
252 obj.tau2_w = (spdiag(1./s_w)*refObj.tau2_w')';
253 obj.tau2_e = (spdiag(1./s_e)*refObj.tau2_e')';
254 obj.tau2_s = (spdiag(1./s_s)*refObj.tau2_s')';
255 obj.tau2_n = (spdiag(1./s_n)*refObj.tau2_n')';
256
257 obj.tau_w = (refObj.e_w'*obj.e1_w*obj.tau1_w')' + (refObj.e_w'*obj.e2_w*obj.tau2_w')';
258 obj.tau_e = (refObj.e_e'*obj.e1_e*obj.tau1_e')' + (refObj.e_e'*obj.e2_e*obj.tau2_e')';
259 obj.tau_s = (refObj.e_s'*obj.e1_s*obj.tau1_s')' + (refObj.e_s'*obj.e2_s*obj.tau2_s')';
260 obj.tau_n = (refObj.e_n'*obj.e1_n*obj.tau1_n')' + (refObj.e_n'*obj.e2_n*obj.tau2_n')';
261
262 % Physical normals
263 e_w = obj.e_scalar_w;
264 e_e = obj.e_scalar_e;
265 e_s = obj.e_scalar_s;
266 e_n = obj.e_scalar_n;
267
268 e_w_vec = obj.e_w;
269 e_e_vec = obj.e_e;
270 e_s_vec = obj.e_s;
271 e_n_vec = obj.e_n;
272
273 nu_w = [-1,0];
274 nu_e = [1,0];
275 nu_s = [0,-1];
276 nu_n = [0,1];
277
278 obj.n_w = cell(2,1);
279 obj.n_e = cell(2,1);
280 obj.n_s = cell(2,1);
281 obj.n_n = cell(2,1);
282
283 n_w_1 = (1./s_w).*e_w'*(J.*(K{1,1}*nu_w(1) + K{1,2}*nu_w(2)));
284 n_w_2 = (1./s_w).*e_w'*(J.*(K{2,1}*nu_w(1) + K{2,2}*nu_w(2)));
285 obj.n_w{1} = spdiag(n_w_1);
286 obj.n_w{2} = spdiag(n_w_2);
287
288 n_e_1 = (1./s_e).*e_e'*(J.*(K{1,1}*nu_e(1) + K{1,2}*nu_e(2)));
289 n_e_2 = (1./s_e).*e_e'*(J.*(K{2,1}*nu_e(1) + K{2,2}*nu_e(2)));
290 obj.n_e{1} = spdiag(n_e_1);
291 obj.n_e{2} = spdiag(n_e_2);
292
293 n_s_1 = (1./s_s).*e_s'*(J.*(K{1,1}*nu_s(1) + K{1,2}*nu_s(2)));
294 n_s_2 = (1./s_s).*e_s'*(J.*(K{2,1}*nu_s(1) + K{2,2}*nu_s(2)));
295 obj.n_s{1} = spdiag(n_s_1);
296 obj.n_s{2} = spdiag(n_s_2);
297
298 n_n_1 = (1./s_n).*e_n'*(J.*(K{1,1}*nu_n(1) + K{1,2}*nu_n(2)));
299 n_n_2 = (1./s_n).*e_n'*(J.*(K{2,1}*nu_n(1) + K{2,2}*nu_n(2)));
300 obj.n_n{1} = spdiag(n_n_1);
301 obj.n_n{2} = spdiag(n_n_2);
302
303 % Operators that extract the normal component
304 obj.en_w = (obj.n_w{1}*obj.e1_w' + obj.n_w{2}*obj.e2_w')';
305 obj.en_e = (obj.n_e{1}*obj.e1_e' + obj.n_e{2}*obj.e2_e')';
306 obj.en_s = (obj.n_s{1}*obj.e1_s' + obj.n_s{2}*obj.e2_s')';
307 obj.en_n = (obj.n_n{1}*obj.e1_n' + obj.n_n{2}*obj.e2_n')';
308
309 % Stress operators
310 sigma = cell(dim, dim);
311 D1 = {obj.Dx, obj.Dy};
312 E = obj.E;
313 N = length(obj.RHO);
314 for i = 1:dim
315 for j = 1:dim
316 sigma{i,j} = sparse(N,2*N);
317 for k = 1:dim
318 for l = 1:dim
319 sigma{i,j} = sigma{i,j} + obj.C{i,j,k,l}*D1{k}*E{l}';
320 end
321 end
322 end
323 end
324 obj.sigma = sigma;
325
326 % Misc.
327 obj.refObj = refObj;
328 obj.m = refObj.m;
329 obj.h = refObj.h;
330 obj.order = order;
331 obj.grid = g;
332 obj.dim = dim;
333
334 end
335
336
337 % Closure functions return the operators applied to the own domain to close the boundary
338 % Penalty functions return the operators to force the solution. In the case of an interface it returns the operator applied to the other doamin.
339 % boundary is a string specifying the boundary e.g. 'l','r' or 'e','w','n','s'.
340 % bc is a cell array of component and bc type, e.g. {1, 'd'} for Dirichlet condition
341 % on the first component. Can also be e.g.
342 % {'normal', 'd'} or {'tangential', 't'} for conditions on
343 % tangential/normal component.
344 % data is a function returning the data that should be applied at the boundary.
345 % neighbour_scheme is an instance of Scheme that should be interfaced to.
346 % neighbour_boundary is a string specifying which boundary to interface to.
347
348 % For displacement bc:
349 % bc = {comp, 'd', dComps},
350 % where
351 % dComps = vector of components with displacement BC. Default: 1:dim.
352 % In this way, we can specify one BC at a time even though the SATs depend on all BC.
353 function [closure, penalty] = boundary_condition(obj, boundary, bc, tuning)
354 default_arg('tuning', 1.0);
355 assert( iscell(bc), 'The BC type must be a 2x1 or 3x1 cell array' );
356
357 [closure, penalty] = obj.refObj.boundary_condition(boundary, bc, tuning);
358
359 type = bc{2};
360
361 switch type
362 case {'F','f','Free','free','traction','Traction','t','T'}
363 s = obj.(['s_' boundary]);
364 s = spdiag(s);
365 penalty = penalty*s;
366 end
367 end
368
369 % type Struct that specifies the interface coupling.
370 % Fields:
371 % -- tuning: penalty strength, defaults to 1.0
372 % -- interpolation: type of interpolation, default 'none'
373 function [closure, penalty] = interface(obj,boundary,neighbour_scheme,neighbour_boundary,type)
374
375 defaultType.tuning = 1.0;
376 defaultType.interpolation = 'none';
377 default_struct('type', defaultType);
378
379 [closure, penalty] = obj.refObj.interface(boundary,neighbour_scheme.refObj,neighbour_boundary,type);
380 end
381
382 % Returns h11 for the boundary specified by the string boundary.
383 % op -- string
384 function h11 = getBorrowing(obj, boundary)
385 assertIsMember(boundary, {'w', 'e', 's', 'n'})
386
387 switch boundary
388 case {'w','e'}
389 h11 = obj.refObj.h11{1};
390 case {'s', 'n'}
391 h11 = obj.refObj.h11{2};
392 end
393 end
394
395 % Returns the outward unit normal vector for the boundary specified by the string boundary.
396 % n is a cell of diagonal matrices for each normal component, n{1} = n_1, n{2} = n_2.
397 function n = getNormal(obj, boundary)
398 assertIsMember(boundary, {'w', 'e', 's', 'n'})
399
400 n = obj.(['n_' boundary]);
401 end
402
403 % Returns the boundary operator op for the boundary specified by the string boundary.
404 % op -- string
405 function o = getBoundaryOperator(obj, op, boundary)
406 assertIsMember(boundary, {'w', 'e', 's', 'n'})
407 assertIsMember(op, {'e', 'e1', 'e2', 'tau', 'tau1', 'tau2', 'en'})
408
409 o = obj.([op, '_', boundary]);
410
411 end
412
413 % Returns the boundary operator op for the boundary specified by the string boundary.
414 % op -- string
415 function o = getBoundaryOperatorForScalarField(obj, op, boundary)
416 assertIsMember(boundary, {'w', 'e', 's', 'n'})
417 assertIsMember(op, {'e'})
418
419 switch op
420
421 case 'e'
422 o = obj.(['e_scalar', '_', boundary]);
423 end
424
425 end
426
427 % Returns the boundary operator T_ij (cell format) for the boundary specified by the string boundary.
428 % Formula: tau_i = T_ij u_j
429 % op -- string
430 function T = getBoundaryTractionOperator(obj, boundary)
431 assertIsMember(boundary, {'w', 'e', 's', 'n'})
432
433 T = obj.(['T', '_', boundary]);
434 end
435
436 % Returns square boundary quadrature matrix, of dimension
437 % corresponding to the number of boundary unknowns
438 %
439 % boundary -- string
440 function H = getBoundaryQuadrature(obj, boundary)
441 assertIsMember(boundary, {'w', 'e', 's', 'n'})
442
443 H = obj.getBoundaryQuadratureForScalarField(boundary);
444 I_dim = speye(obj.dim, obj.dim);
445 H = kron(H, I_dim);
446 end
447
448 % Returns square boundary quadrature matrix, of dimension
449 % corresponding to the number of boundary grid points
450 %
451 % boundary -- string
452 function H_b = getBoundaryQuadratureForScalarField(obj, boundary)
453 assertIsMember(boundary, {'w', 'e', 's', 'n'})
454
455 H_b = obj.(['H_', boundary]);
456 end
457
458 function N = size(obj)
459 N = obj.dim*prod(obj.m);
460 end
461 end
462 end