comparison +scheme/ViscoElastic2d.m @ 1307:fcca6ad8b102 feature/poroelastic

Add diffOp for viscoElastic
author Martin Almquist <malmquist@stanford.edu>
date Sun, 19 Jul 2020 20:30:16 -0700
parents
children 5016f3f3badb
comparison
equal deleted inserted replaced
1306:633757e582e5 1307:fcca6ad8b102
1 classdef ViscoElastic2d < scheme.Scheme
2
3 % Discretizes the visco-elastic wave equation in curvilinear coordinates.
4 % Assumes fully compatible operators.
5
6 properties
7 m % Number of points in each direction, possibly a vector
8 h % Grid spacing
9
10 grid
11 dim
12
13 order % Order of accuracy for the approximation
14
15 % Diagonal matrices for variable coefficients
16 % J, Ji
17 RHO % Density
18 C % Elastic stiffness tensor
19 ETA % Effective viscosity, used in strain rate eq
20
21 D % Total operator
22 Delastic % Elastic operator (momentum balance)
23 Dviscous % Acts on viscous strains in momentum balance
24 DstrainRate % Acts on u and gamma, returns strain rate gamma_t
25
26 D1, D1Tilde % Physical derivatives
27 sigma % Cell matrix of physical stress operators
28
29 % Inner products
30 H
31
32 % Boundary inner products (for scalar field)
33 H_w, H_e, H_s, H_n
34
35 % Restriction operators
36 Eu, Egamma % Pick out all components of u/gamma
37 eU, eGamma % Pick out one specific component
38
39 % Bundary restriction ops
40 e_scalar_w, e_scalar_e, e_scalar_s, e_scalar_n
41
42 n_w, n_e, n_s, n_n % Physical normals
43
44 elasticObj
45
46 end
47
48 methods
49
50 % The coefficients can either be function handles or grid functions
51 function obj = ViscoElastic2d(g, order, rho, C, eta)
52 default_arg('rho', @(x,y) 0*x+1);
53 default_arg('eta', @(x,y) 0*x+1);
54 dim = 2;
55
56 C_default = cell(dim,dim,dim,dim);
57 for i = 1:dim
58 for j = 1:dim
59 for k = 1:dim
60 for l = 1:dim
61 C_default{i,j,k,l} = @(x,y) 0*x ;
62 end
63 end
64 end
65 end
66 default_arg('C', C_default);
67
68 assert(isa(g, 'grid.Curvilinear'));
69
70 if isa(rho, 'function_handle')
71 rho = grid.evalOn(g, rho);
72 end
73
74 if isa(eta, 'function_handle')
75 eta = grid.evalOn(g, eta);
76 end
77
78 C_mat = cell(dim,dim,dim,dim);
79 for i = 1:dim
80 for j = 1:dim
81 for k = 1:dim
82 for l = 1:dim
83 if isa(C{i,j,k,l}, 'function_handle')
84 C{i,j,k,l} = grid.evalOn(g, C{i,j,k,l});
85 end
86 C_mat{i,j,k,l} = spdiag(C{i,j,k,l});
87 end
88 end
89 end
90 end
91 obj.C = C_mat;
92
93 elasticObj = scheme.Elastic2dCurvilinearAnisotropic(g, order, rho, C);
94
95 % Construct a pair of first derivatives
96 K = elasticObj.K;
97 for i = 1:dim
98 for j = 1:dim
99 K{i,j} = spdiag(K{i,j});
100 end
101 end
102 J = elasticObj.J;
103 Ji = elasticObj.Ji;
104 D_ref = elasticObj.refObj.D1;
105 D1 = cell(dim, 1);
106 D1Tilde = cell(dim, 1);
107 for i = 1:dim
108 D1{i} = 0*D_ref{i};
109 D1Tilde{i} = 0*D_ref{i};
110 for j = 1:dim
111 D1{i} = D1{i} + K{i,j}*D_ref{j};
112 D1Tilde{i} = D1Tilde{i} + Ji*D_ref{j}*J*K{i,j};
113 end
114 end
115 obj.D1 = D1;
116 obj.D1Tilde = D1Tilde;
117
118 eU = elasticObj.E;
119
120 % Storage order for gamma: 11-12-21-22.
121 I = speye(g.N(), g.N());
122 eGamma = cell(dim, dim);
123 e = cell(dim, dim);
124 e{1,1} = [1;0;0;0];
125 e{1,2} = [0;1;0;0];
126 e{2,1} = [0;0;1;0];
127 e{2,2} = [0;0;0;1];
128 for i = 1:dim
129 for j = 1:dim
130 eGamma{i,j} = kron(I, e{i,j});
131 end
132 end
133
134 % Store u first, then gamma
135 mU = dim*g.N();
136 mGamma = dim^2*g.N();
137 Iu = speye(mU, mU);
138 Igamma = speye(mGamma, mGamma);
139
140 Eu = cell2mat({Iu, sparse(mU, mGamma)})';
141 Egamma = cell2mat({sparse(mGamma, mU), Igamma})';
142
143 for i = 1:dim
144 eU{i} = Eu*eU{i};
145 end
146 for i = 1:dim
147 for j = 1:dim
148 eGamma{i,j} = Egamma*eGamma{i,j};
149 end
150 end
151
152 obj.eGamma = eGamma;
153 obj.eU = eU;
154 obj.Egamma = Egamma;
155 obj.Eu = Eu;
156
157 % Build stress operator
158 sigma = cell(dim, dim);
159 C = obj.C;
160 for i = 1:dim
161 for j = 1:dim
162 sigma{i,j} = spalloc(g.N(), (dim^2 + dim)*g.N(), order^2*g.N());
163 for k = 1:dim
164 for l = 1:dim
165 sigma{i,j} = sigma{i,j} + C{i,j,k,l}*(D1{k}*eU{l}' - eGamma{k,l}');
166 end
167 end
168 end
169 end
170
171 % Elastic operator
172 Delastic = Eu*elasticObj.D*Eu';
173
174 % Add viscous strains to momentum balance
175 RHOi = spdiag(1./rho);
176 Dviscous = spalloc((dim^2 + dim)*g.N(), (dim^2 + dim)*g.N(), order^2*(dim^2 + dim)*g.N());
177 for i = 1:dim
178 for j = 1:dim
179 for k = 1:dim
180 for l = 1:dim
181 Dviscous = Dviscous - eU{j}*RHOi*D1Tilde{i}*C{i,j,k,l}*eGamma{k,l}';
182 end
183 end
184 end
185 end
186
187 ETA = spdiag(eta);
188 DstrainRate = 0*Delastic;
189 for i = 1:dim
190 for j = 1:dim
191 DstrainRate = DstrainRate + eGamma{i,j}*(ETA\sigma{i,j});
192 end
193 end
194
195 obj.D = Delastic + Dviscous + DstrainRate;
196 obj.Delastic = Delastic;
197 obj.Dviscous = Dviscous;
198 obj.DstrainRate = DstrainRate;
199 obj.sigma = sigma;
200
201 %---- Set remaining object properties ------
202 obj.RHO = elasticObj.RHO;
203 obj.ETA = ETA;
204 obj.H = elasticObj.H;
205
206 obj.n_w = elasticObj.n_w;
207 obj.n_e = elasticObj.n_e;
208 obj.n_s = elasticObj.n_s;
209 obj.n_n = elasticObj.n_n;
210
211 obj.H_w = elasticObj.H_w;
212 obj.H_e = elasticObj.H_e;
213 obj.H_s = elasticObj.H_s;
214 obj.H_n = elasticObj.H_n;
215
216 obj.e_scalar_w = elasticObj.e_scalar_w;
217 obj.e_scalar_e = elasticObj.e_scalar_e;
218 obj.e_scalar_s = elasticObj.e_scalar_s;
219 obj.e_scalar_n = elasticObj.e_scalar_n;
220
221 % Misc.
222 obj.elasticObj = elasticObj;
223 obj.m = elasticObj.m;
224 obj.h = elasticObj.h;
225
226 obj.order = order;
227 obj.grid = g;
228 obj.dim = dim;
229
230 end
231
232
233 % Closure functions return the operators applied to the own domain to close the boundary
234 % Penalty functions return the operators to force the solution. In the case of an interface it returns the operator applied to the other doamin.
235 % boundary is a string specifying the boundary e.g. 'l','r' or 'e','w','n','s'.
236 % bc is a cell array of component and bc type, e.g. {1, 'd'} for Dirichlet condition
237 % on the first component. Can also be e.g.
238 % {'normal', 'd'} or {'tangential', 't'} for conditions on
239 % tangential/normal component.
240 % data is a function returning the data that should be applied at the boundary.
241 % neighbour_scheme is an instance of Scheme that should be interfaced to.
242 % neighbour_boundary is a string specifying which boundary to interface to.
243
244 % For displacement bc:
245 % bc = {comp, 'd', dComps},
246 % where
247 % dComps = vector of components with displacement BC. Default: 1:dim.
248 % In this way, we can specify one BC at a time even though the SATs depend on all BC.
249 function [closure, penalty] = boundary_condition(obj, boundary, bc, tuning)
250 default_arg('tuning', 1.0);
251 assert( iscell(bc), 'The BC type must be a 2x1 or 3x1 cell array' );
252
253 component = bc{1};
254 type = bc{2};
255 dim = obj.dim;
256
257 n = obj.getNormal(boundary);
258 H_gamma = obj.getBoundaryQuadratureForScalarField(boundary);
259 e = obj.getBoundaryOperatorForScalarField('e', boundary);
260
261 H = obj.H;
262 RHO = obj.RHO;
263 C = obj.C;
264 Eu = obj.Eu;
265 eU = obj.eU;
266 eGamma = obj.eGamma;
267
268 switch type
269 case {'F','f','Free','free','traction','Traction','t','T'}
270 [closure, penalty] = obj.elasticObj.boundary_condition(boundary, bc, tuning);
271 closure = Eu*closure*Eu';
272 penalty = Eu*penalty;
273
274 j = component;
275 for i = 1:dim
276 for k = 1:dim
277 for l = 1:dim
278 closure = closure + eU{j}*( (RHO*H)\(C{i,j,k,l}*e*H_gamma*n{i}*e'*eGamma{k,l}') );
279 end
280 end
281 end
282 end
283
284 end
285
286 function [closure, penalty] = displacementBCNormalTangential(obj, boundary, bc, tuning)
287 disp('WARNING: displacementBCNormalTangential is only guaranteed to work for displacement BC on one component and traction on the other');
288 u = obj;
289
290 component = bc{1};
291 type = bc{2};
292
293 switch component
294 case 'n'
295 en = u.getBoundaryOperator('en', boundary);
296 tau_n = u.getBoundaryOperator('tau_n', boundary);
297 N = u.getNormal(boundary);
298 case 't'
299 en = u.getBoundaryOperator('et', boundary);
300 tau_n = u.getBoundaryOperator('tau_t', boundary);
301 N = u.getTangent(boundary);
302 end
303
304 % Operators
305 e = u.getBoundaryOperatorForScalarField('e', boundary);
306 h11 = u.getBorrowing(boundary);
307 n = u.getNormal(boundary);
308
309 C = u.C;
310 Ji = u.Ji;
311 s = spdiag(u.(['s_' boundary]));
312 m_tot = u.grid.N();
313
314 Hi = u.E{1}*inv(u.H)*u.E{1}' + u.E{2}*inv(u.H)*u.E{2}';
315 RHOi = u.E{1}*inv(u.RHO)*u.E{1}' + u.E{2}*inv(u.RHO)*u.E{2}';
316
317 H_gamma = u.getBoundaryQuadratureForScalarField(boundary);
318 dim = u.dim;
319
320 % Preallocate
321 [~, m_int] = size(H_gamma);
322 closure = sparse(dim*m_tot, dim*m_tot);
323 penalty = sparse(dim*m_tot, m_int);
324
325 % Term 1: The symmetric term
326 Z = sparse(m_int, m_int);
327 for i = 1:dim
328 for j = 1:dim
329 for l = 1:dim
330 for k = 1:dim
331 Z = Z + n{i}*N{j}*e'*Ji*C{i,j,k,l}*e*n{k}*N{l};
332 end
333 end
334 end
335 end
336
337 Z = -tuning*dim*1/h11*s*Z;
338 closure = closure + en*H_gamma*Z*en';
339 penalty = penalty - en*H_gamma*Z;
340
341 % Term 2: The symmetrizing term
342 closure = closure + tau_n*H_gamma*en';
343 penalty = penalty - tau_n*H_gamma;
344
345 % Multiply all normal component terms by inverse of density x quadraure
346 closure = RHOi*Hi*closure;
347 penalty = RHOi*Hi*penalty;
348 end
349
350 % type Struct that specifies the interface coupling.
351 % Fields:
352 % -- tuning: penalty strength, defaults to 1.0
353 % -- interpolation: type of interpolation, default 'none'
354 function [closure, penalty] = interface(obj,boundary,neighbour_scheme,neighbour_boundary,type)
355
356 defaultType.tuning = 1.0;
357 defaultType.interpolation = 'none';
358 defaultType.type = 'standard';
359 default_struct('type', defaultType);
360
361 switch type.type
362 case 'standard'
363 [closure, penalty] = obj.refObj.interface(boundary,neighbour_scheme.refObj,neighbour_boundary,type);
364 case 'frictionalFault'
365 [closure, penalty] = obj.interfaceFrictionalFault(boundary,neighbour_scheme,neighbour_boundary,type);
366 end
367
368 end
369
370 function [closure, penalty] = interfaceFrictionalFault(obj,boundary,neighbour_scheme,neighbour_boundary,type)
371 tuning = type.tuning;
372
373 % u denotes the solution in the own domain
374 % v denotes the solution in the neighbour domain
375
376 u = obj;
377 v = neighbour_scheme;
378
379 % Operators, u side
380 e_u = u.getBoundaryOperatorForScalarField('e', boundary);
381 en_u = u.getBoundaryOperator('en', boundary);
382 tau_n_u = u.getBoundaryOperator('tau_n', boundary);
383 h11_u = u.getBorrowing(boundary);
384 n_u = u.getNormal(boundary);
385
386 C_u = u.C;
387 Ji_u = u.Ji;
388 s_u = spdiag(u.(['s_' boundary]));
389 m_tot_u = u.grid.N();
390
391 % Operators, v side
392 e_v = v.getBoundaryOperatorForScalarField('e', neighbour_boundary);
393 en_v = v.getBoundaryOperator('en', neighbour_boundary);
394 tau_n_v = v.getBoundaryOperator('tau_n', neighbour_boundary);
395 h11_v = v.getBorrowing(neighbour_boundary);
396 n_v = v.getNormal(neighbour_boundary);
397
398 C_v = v.C;
399 Ji_v = v.Ji;
400 s_v = spdiag(v.(['s_' neighbour_boundary]));
401 m_tot_v = v.grid.N();
402
403 % Operators that are only required for own domain
404 Hi = u.E{1}*inv(u.H)*u.E{1}' + u.E{2}*inv(u.H)*u.E{2}';
405 RHOi = u.E{1}*inv(u.RHO)*u.E{1}' + u.E{2}*inv(u.RHO)*u.E{2}';
406
407 % Shared operators
408 H_gamma = u.getBoundaryQuadratureForScalarField(boundary);
409 dim = u.dim;
410
411 % Preallocate
412 [~, m_int] = size(H_gamma);
413 closure = sparse(dim*m_tot_u, dim*m_tot_u);
414 penalty = sparse(dim*m_tot_u, dim*m_tot_v);
415
416 % Continuity of normal displacement, term 1: The symmetric term
417 Z_u = sparse(m_int, m_int);
418 Z_v = sparse(m_int, m_int);
419 for i = 1:dim
420 for j = 1:dim
421 for l = 1:dim
422 for k = 1:dim
423 Z_u = Z_u + n_u{i}*n_u{j}*e_u'*Ji_u*C_u{i,j,k,l}*e_u*n_u{k}*n_u{l};
424 Z_v = Z_v + n_v{i}*n_v{j}*e_v'*Ji_v*C_v{i,j,k,l}*e_v*n_v{k}*n_v{l};
425 end
426 end
427 end
428 end
429
430 Z = -tuning*dim*( 1/(4*h11_u)*s_u*Z_u + 1/(4*h11_v)*s_v*Z_v );
431 closure = closure + en_u*H_gamma*Z*en_u';
432 penalty = penalty + en_u*H_gamma*Z*en_v';
433
434 % Continuity of normal displacement, term 2: The symmetrizing term
435 closure = closure + 1/2*tau_n_u*H_gamma*en_u';
436 penalty = penalty + 1/2*tau_n_u*H_gamma*en_v';
437
438 % Continuity of normal traction
439 closure = closure - 1/2*en_u*H_gamma*tau_n_u';
440 penalty = penalty + 1/2*en_u*H_gamma*tau_n_v';
441
442 % Multiply all normal component terms by inverse of density x quadraure
443 closure = RHOi*Hi*closure;
444 penalty = RHOi*Hi*penalty;
445
446 % ---- Tangential tractions are imposed just like traction BC ------
447 closure = closure + obj.boundary_condition(boundary, {'t', 't'});
448
449 end
450
451
452 % Returns h11 for the boundary specified by the string boundary.
453 % op -- string
454 function h11 = getBorrowing(obj, boundary)
455 assertIsMember(boundary, {'w', 'e', 's', 'n'})
456
457 switch boundary
458 case {'w','e'}
459 h11 = obj.refObj.h11{1};
460 case {'s', 'n'}
461 h11 = obj.refObj.h11{2};
462 end
463 end
464
465 % Returns the outward unit normal vector for the boundary specified by the string boundary.
466 % n is a cell of diagonal matrices for each normal component, n{1} = n_1, n{2} = n_2.
467 function n = getNormal(obj, boundary)
468 assertIsMember(boundary, {'w', 'e', 's', 'n'})
469
470 n = obj.(['n_' boundary]);
471 end
472
473 % Returns the unit tangent vector for the boundary specified by the string boundary.
474 % t is a cell of diagonal matrices for each normal component, t{1} = t_1, t{2} = t_2.
475 function t = getTangent(obj, boundary)
476 assertIsMember(boundary, {'w', 'e', 's', 'n'})
477
478 t = obj.(['tangent_' boundary]);
479 end
480
481 % Returns the boundary operator op for the boundary specified by the string boundary.
482 % op -- string
483 function o = getBoundaryOperator(obj, op, boundary)
484 assertIsMember(boundary, {'w', 'e', 's', 'n'})
485 assertIsMember(op, {'e', 'e1', 'e2', 'tau', 'tau1', 'tau2', 'en', 'et', 'tau_n', 'tau_t'})
486
487 o = obj.([op, '_', boundary]);
488
489 end
490
491 % Returns the boundary operator op for the boundary specified by the string boundary.
492 % op -- string
493 function o = getBoundaryOperatorForScalarField(obj, op, boundary)
494 assertIsMember(boundary, {'w', 'e', 's', 'n'})
495 assertIsMember(op, {'e'})
496
497 switch op
498
499 case 'e'
500 o = obj.(['e_scalar', '_', boundary]);
501 end
502
503 end
504
505 % Returns the boundary operator T_ij (cell format) for the boundary specified by the string boundary.
506 % Formula: tau_i = T_ij u_j
507 % op -- string
508 function T = getBoundaryTractionOperator(obj, boundary)
509 assertIsMember(boundary, {'w', 'e', 's', 'n'})
510
511 T = obj.(['T', '_', boundary]);
512 end
513
514 % Returns square boundary quadrature matrix, of dimension
515 % corresponding to the number of boundary unknowns
516 %
517 % boundary -- string
518 function H = getBoundaryQuadrature(obj, boundary)
519 assertIsMember(boundary, {'w', 'e', 's', 'n'})
520
521 H = obj.getBoundaryQuadratureForScalarField(boundary);
522 I_dim = speye(obj.dim, obj.dim);
523 H = kron(H, I_dim);
524 end
525
526 % Returns square boundary quadrature matrix, of dimension
527 % corresponding to the number of boundary grid points
528 %
529 % boundary -- string
530 function H_b = getBoundaryQuadratureForScalarField(obj, boundary)
531 assertIsMember(boundary, {'w', 'e', 's', 'n'})
532
533 H_b = obj.(['H_', boundary]);
534 end
535
536 function N = size(obj)
537 N = (obj.dim + obj.dim^2)*prod(obj.m);
538 end
539 end
540 end