Mercurial > repos > public > sbplib
changeset 1307:fcca6ad8b102 feature/poroelastic
Add diffOp for viscoElastic
author | Martin Almquist <malmquist@stanford.edu> |
---|---|
date | Sun, 19 Jul 2020 20:30:16 -0700 |
parents | 633757e582e5 |
children | 5016f3f3badb |
files | +scheme/ViscoElastic2d.m |
diffstat | 1 files changed, 540 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
diff -r 633757e582e5 -r fcca6ad8b102 +scheme/ViscoElastic2d.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/+scheme/ViscoElastic2d.m Sun Jul 19 20:30:16 2020 -0700 @@ -0,0 +1,540 @@ +classdef ViscoElastic2d < scheme.Scheme + +% Discretizes the visco-elastic wave equation in curvilinear coordinates. +% Assumes fully compatible operators. + + properties + m % Number of points in each direction, possibly a vector + h % Grid spacing + + grid + dim + + order % Order of accuracy for the approximation + + % Diagonal matrices for variable coefficients + % J, Ji + RHO % Density + C % Elastic stiffness tensor + ETA % Effective viscosity, used in strain rate eq + + D % Total operator + Delastic % Elastic operator (momentum balance) + Dviscous % Acts on viscous strains in momentum balance + DstrainRate % Acts on u and gamma, returns strain rate gamma_t + + D1, D1Tilde % Physical derivatives + sigma % Cell matrix of physical stress operators + + % Inner products + H + + % Boundary inner products (for scalar field) + H_w, H_e, H_s, H_n + + % Restriction operators + Eu, Egamma % Pick out all components of u/gamma + eU, eGamma % Pick out one specific component + + % Bundary restriction ops + e_scalar_w, e_scalar_e, e_scalar_s, e_scalar_n + + n_w, n_e, n_s, n_n % Physical normals + + elasticObj + + end + + methods + + % The coefficients can either be function handles or grid functions + function obj = ViscoElastic2d(g, order, rho, C, eta) + default_arg('rho', @(x,y) 0*x+1); + default_arg('eta', @(x,y) 0*x+1); + dim = 2; + + C_default = cell(dim,dim,dim,dim); + for i = 1:dim + for j = 1:dim + for k = 1:dim + for l = 1:dim + C_default{i,j,k,l} = @(x,y) 0*x ; + end + end + end + end + default_arg('C', C_default); + + assert(isa(g, 'grid.Curvilinear')); + + if isa(rho, 'function_handle') + rho = grid.evalOn(g, rho); + end + + if isa(eta, 'function_handle') + eta = grid.evalOn(g, eta); + end + + C_mat = cell(dim,dim,dim,dim); + for i = 1:dim + for j = 1:dim + for k = 1:dim + for l = 1:dim + if isa(C{i,j,k,l}, 'function_handle') + C{i,j,k,l} = grid.evalOn(g, C{i,j,k,l}); + end + C_mat{i,j,k,l} = spdiag(C{i,j,k,l}); + end + end + end + end + obj.C = C_mat; + + elasticObj = scheme.Elastic2dCurvilinearAnisotropic(g, order, rho, C); + + % Construct a pair of first derivatives + K = elasticObj.K; + for i = 1:dim + for j = 1:dim + K{i,j} = spdiag(K{i,j}); + end + end + J = elasticObj.J; + Ji = elasticObj.Ji; + D_ref = elasticObj.refObj.D1; + D1 = cell(dim, 1); + D1Tilde = cell(dim, 1); + for i = 1:dim + D1{i} = 0*D_ref{i}; + D1Tilde{i} = 0*D_ref{i}; + for j = 1:dim + D1{i} = D1{i} + K{i,j}*D_ref{j}; + D1Tilde{i} = D1Tilde{i} + Ji*D_ref{j}*J*K{i,j}; + end + end + obj.D1 = D1; + obj.D1Tilde = D1Tilde; + + eU = elasticObj.E; + + % Storage order for gamma: 11-12-21-22. + I = speye(g.N(), g.N()); + eGamma = cell(dim, dim); + e = cell(dim, dim); + e{1,1} = [1;0;0;0]; + e{1,2} = [0;1;0;0]; + e{2,1} = [0;0;1;0]; + e{2,2} = [0;0;0;1]; + for i = 1:dim + for j = 1:dim + eGamma{i,j} = kron(I, e{i,j}); + end + end + + % Store u first, then gamma + mU = dim*g.N(); + mGamma = dim^2*g.N(); + Iu = speye(mU, mU); + Igamma = speye(mGamma, mGamma); + + Eu = cell2mat({Iu, sparse(mU, mGamma)})'; + Egamma = cell2mat({sparse(mGamma, mU), Igamma})'; + + for i = 1:dim + eU{i} = Eu*eU{i}; + end + for i = 1:dim + for j = 1:dim + eGamma{i,j} = Egamma*eGamma{i,j}; + end + end + + obj.eGamma = eGamma; + obj.eU = eU; + obj.Egamma = Egamma; + obj.Eu = Eu; + + % Build stress operator + sigma = cell(dim, dim); + C = obj.C; + for i = 1:dim + for j = 1:dim + sigma{i,j} = spalloc(g.N(), (dim^2 + dim)*g.N(), order^2*g.N()); + for k = 1:dim + for l = 1:dim + sigma{i,j} = sigma{i,j} + C{i,j,k,l}*(D1{k}*eU{l}' - eGamma{k,l}'); + end + end + end + end + + % Elastic operator + Delastic = Eu*elasticObj.D*Eu'; + + % Add viscous strains to momentum balance + RHOi = spdiag(1./rho); + Dviscous = spalloc((dim^2 + dim)*g.N(), (dim^2 + dim)*g.N(), order^2*(dim^2 + dim)*g.N()); + for i = 1:dim + for j = 1:dim + for k = 1:dim + for l = 1:dim + Dviscous = Dviscous - eU{j}*RHOi*D1Tilde{i}*C{i,j,k,l}*eGamma{k,l}'; + end + end + end + end + + ETA = spdiag(eta); + DstrainRate = 0*Delastic; + for i = 1:dim + for j = 1:dim + DstrainRate = DstrainRate + eGamma{i,j}*(ETA\sigma{i,j}); + end + end + + obj.D = Delastic + Dviscous + DstrainRate; + obj.Delastic = Delastic; + obj.Dviscous = Dviscous; + obj.DstrainRate = DstrainRate; + obj.sigma = sigma; + + %---- Set remaining object properties ------ + obj.RHO = elasticObj.RHO; + obj.ETA = ETA; + obj.H = elasticObj.H; + + obj.n_w = elasticObj.n_w; + obj.n_e = elasticObj.n_e; + obj.n_s = elasticObj.n_s; + obj.n_n = elasticObj.n_n; + + obj.H_w = elasticObj.H_w; + obj.H_e = elasticObj.H_e; + obj.H_s = elasticObj.H_s; + obj.H_n = elasticObj.H_n; + + obj.e_scalar_w = elasticObj.e_scalar_w; + obj.e_scalar_e = elasticObj.e_scalar_e; + obj.e_scalar_s = elasticObj.e_scalar_s; + obj.e_scalar_n = elasticObj.e_scalar_n; + + % Misc. + obj.elasticObj = elasticObj; + obj.m = elasticObj.m; + obj.h = elasticObj.h; + + obj.order = order; + obj.grid = g; + obj.dim = dim; + + end + + + % Closure functions return the operators applied to the own domain to close the boundary + % Penalty functions return the operators to force the solution. In the case of an interface it returns the operator applied to the other doamin. + % boundary is a string specifying the boundary e.g. 'l','r' or 'e','w','n','s'. + % bc is a cell array of component and bc type, e.g. {1, 'd'} for Dirichlet condition + % on the first component. Can also be e.g. + % {'normal', 'd'} or {'tangential', 't'} for conditions on + % tangential/normal component. + % data is a function returning the data that should be applied at the boundary. + % neighbour_scheme is an instance of Scheme that should be interfaced to. + % neighbour_boundary is a string specifying which boundary to interface to. + + % For displacement bc: + % bc = {comp, 'd', dComps}, + % where + % dComps = vector of components with displacement BC. Default: 1:dim. + % In this way, we can specify one BC at a time even though the SATs depend on all BC. + function [closure, penalty] = boundary_condition(obj, boundary, bc, tuning) + default_arg('tuning', 1.0); + assert( iscell(bc), 'The BC type must be a 2x1 or 3x1 cell array' ); + + component = bc{1}; + type = bc{2}; + dim = obj.dim; + + n = obj.getNormal(boundary); + H_gamma = obj.getBoundaryQuadratureForScalarField(boundary); + e = obj.getBoundaryOperatorForScalarField('e', boundary); + + H = obj.H; + RHO = obj.RHO; + C = obj.C; + Eu = obj.Eu; + eU = obj.eU; + eGamma = obj.eGamma; + + switch type + case {'F','f','Free','free','traction','Traction','t','T'} + [closure, penalty] = obj.elasticObj.boundary_condition(boundary, bc, tuning); + closure = Eu*closure*Eu'; + penalty = Eu*penalty; + + j = component; + for i = 1:dim + for k = 1:dim + for l = 1:dim + closure = closure + eU{j}*( (RHO*H)\(C{i,j,k,l}*e*H_gamma*n{i}*e'*eGamma{k,l}') ); + end + end + end + end + + end + + function [closure, penalty] = displacementBCNormalTangential(obj, boundary, bc, tuning) + disp('WARNING: displacementBCNormalTangential is only guaranteed to work for displacement BC on one component and traction on the other'); + u = obj; + + component = bc{1}; + type = bc{2}; + + switch component + case 'n' + en = u.getBoundaryOperator('en', boundary); + tau_n = u.getBoundaryOperator('tau_n', boundary); + N = u.getNormal(boundary); + case 't' + en = u.getBoundaryOperator('et', boundary); + tau_n = u.getBoundaryOperator('tau_t', boundary); + N = u.getTangent(boundary); + end + + % Operators + e = u.getBoundaryOperatorForScalarField('e', boundary); + h11 = u.getBorrowing(boundary); + n = u.getNormal(boundary); + + C = u.C; + Ji = u.Ji; + s = spdiag(u.(['s_' boundary])); + m_tot = u.grid.N(); + + Hi = u.E{1}*inv(u.H)*u.E{1}' + u.E{2}*inv(u.H)*u.E{2}'; + RHOi = u.E{1}*inv(u.RHO)*u.E{1}' + u.E{2}*inv(u.RHO)*u.E{2}'; + + H_gamma = u.getBoundaryQuadratureForScalarField(boundary); + dim = u.dim; + + % Preallocate + [~, m_int] = size(H_gamma); + closure = sparse(dim*m_tot, dim*m_tot); + penalty = sparse(dim*m_tot, m_int); + + % Term 1: The symmetric term + Z = sparse(m_int, m_int); + for i = 1:dim + for j = 1:dim + for l = 1:dim + for k = 1:dim + Z = Z + n{i}*N{j}*e'*Ji*C{i,j,k,l}*e*n{k}*N{l}; + end + end + end + end + + Z = -tuning*dim*1/h11*s*Z; + closure = closure + en*H_gamma*Z*en'; + penalty = penalty - en*H_gamma*Z; + + % Term 2: The symmetrizing term + closure = closure + tau_n*H_gamma*en'; + penalty = penalty - tau_n*H_gamma; + + % Multiply all normal component terms by inverse of density x quadraure + closure = RHOi*Hi*closure; + penalty = RHOi*Hi*penalty; + end + + % type Struct that specifies the interface coupling. + % Fields: + % -- tuning: penalty strength, defaults to 1.0 + % -- interpolation: type of interpolation, default 'none' + function [closure, penalty] = interface(obj,boundary,neighbour_scheme,neighbour_boundary,type) + + defaultType.tuning = 1.0; + defaultType.interpolation = 'none'; + defaultType.type = 'standard'; + default_struct('type', defaultType); + + switch type.type + case 'standard' + [closure, penalty] = obj.refObj.interface(boundary,neighbour_scheme.refObj,neighbour_boundary,type); + case 'frictionalFault' + [closure, penalty] = obj.interfaceFrictionalFault(boundary,neighbour_scheme,neighbour_boundary,type); + end + + end + + function [closure, penalty] = interfaceFrictionalFault(obj,boundary,neighbour_scheme,neighbour_boundary,type) + tuning = type.tuning; + + % u denotes the solution in the own domain + % v denotes the solution in the neighbour domain + + u = obj; + v = neighbour_scheme; + + % Operators, u side + e_u = u.getBoundaryOperatorForScalarField('e', boundary); + en_u = u.getBoundaryOperator('en', boundary); + tau_n_u = u.getBoundaryOperator('tau_n', boundary); + h11_u = u.getBorrowing(boundary); + n_u = u.getNormal(boundary); + + C_u = u.C; + Ji_u = u.Ji; + s_u = spdiag(u.(['s_' boundary])); + m_tot_u = u.grid.N(); + + % Operators, v side + e_v = v.getBoundaryOperatorForScalarField('e', neighbour_boundary); + en_v = v.getBoundaryOperator('en', neighbour_boundary); + tau_n_v = v.getBoundaryOperator('tau_n', neighbour_boundary); + h11_v = v.getBorrowing(neighbour_boundary); + n_v = v.getNormal(neighbour_boundary); + + C_v = v.C; + Ji_v = v.Ji; + s_v = spdiag(v.(['s_' neighbour_boundary])); + m_tot_v = v.grid.N(); + + % Operators that are only required for own domain + Hi = u.E{1}*inv(u.H)*u.E{1}' + u.E{2}*inv(u.H)*u.E{2}'; + RHOi = u.E{1}*inv(u.RHO)*u.E{1}' + u.E{2}*inv(u.RHO)*u.E{2}'; + + % Shared operators + H_gamma = u.getBoundaryQuadratureForScalarField(boundary); + dim = u.dim; + + % Preallocate + [~, m_int] = size(H_gamma); + closure = sparse(dim*m_tot_u, dim*m_tot_u); + penalty = sparse(dim*m_tot_u, dim*m_tot_v); + + % Continuity of normal displacement, term 1: The symmetric term + Z_u = sparse(m_int, m_int); + Z_v = sparse(m_int, m_int); + for i = 1:dim + for j = 1:dim + for l = 1:dim + for k = 1:dim + 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}; + 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}; + end + end + end + end + + Z = -tuning*dim*( 1/(4*h11_u)*s_u*Z_u + 1/(4*h11_v)*s_v*Z_v ); + closure = closure + en_u*H_gamma*Z*en_u'; + penalty = penalty + en_u*H_gamma*Z*en_v'; + + % Continuity of normal displacement, term 2: The symmetrizing term + closure = closure + 1/2*tau_n_u*H_gamma*en_u'; + penalty = penalty + 1/2*tau_n_u*H_gamma*en_v'; + + % Continuity of normal traction + closure = closure - 1/2*en_u*H_gamma*tau_n_u'; + penalty = penalty + 1/2*en_u*H_gamma*tau_n_v'; + + % Multiply all normal component terms by inverse of density x quadraure + closure = RHOi*Hi*closure; + penalty = RHOi*Hi*penalty; + + % ---- Tangential tractions are imposed just like traction BC ------ + closure = closure + obj.boundary_condition(boundary, {'t', 't'}); + + end + + + % Returns h11 for the boundary specified by the string boundary. + % op -- string + function h11 = getBorrowing(obj, boundary) + assertIsMember(boundary, {'w', 'e', 's', 'n'}) + + switch boundary + case {'w','e'} + h11 = obj.refObj.h11{1}; + case {'s', 'n'} + h11 = obj.refObj.h11{2}; + end + end + + % Returns the outward unit normal vector for the boundary specified by the string boundary. + % n is a cell of diagonal matrices for each normal component, n{1} = n_1, n{2} = n_2. + function n = getNormal(obj, boundary) + assertIsMember(boundary, {'w', 'e', 's', 'n'}) + + n = obj.(['n_' boundary]); + end + + % Returns the unit tangent vector for the boundary specified by the string boundary. + % t is a cell of diagonal matrices for each normal component, t{1} = t_1, t{2} = t_2. + function t = getTangent(obj, boundary) + assertIsMember(boundary, {'w', 'e', 's', 'n'}) + + t = obj.(['tangent_' boundary]); + end + + % Returns the boundary operator op for the boundary specified by the string boundary. + % op -- string + function o = getBoundaryOperator(obj, op, boundary) + assertIsMember(boundary, {'w', 'e', 's', 'n'}) + assertIsMember(op, {'e', 'e1', 'e2', 'tau', 'tau1', 'tau2', 'en', 'et', 'tau_n', 'tau_t'}) + + o = obj.([op, '_', boundary]); + + end + + % Returns the boundary operator op for the boundary specified by the string boundary. + % op -- string + function o = getBoundaryOperatorForScalarField(obj, op, boundary) + assertIsMember(boundary, {'w', 'e', 's', 'n'}) + assertIsMember(op, {'e'}) + + switch op + + case 'e' + o = obj.(['e_scalar', '_', boundary]); + end + + end + + % Returns the boundary operator T_ij (cell format) for the boundary specified by the string boundary. + % Formula: tau_i = T_ij u_j + % op -- string + function T = getBoundaryTractionOperator(obj, boundary) + assertIsMember(boundary, {'w', 'e', 's', 'n'}) + + T = obj.(['T', '_', boundary]); + end + + % Returns square boundary quadrature matrix, of dimension + % corresponding to the number of boundary unknowns + % + % boundary -- string + function H = getBoundaryQuadrature(obj, boundary) + assertIsMember(boundary, {'w', 'e', 's', 'n'}) + + H = obj.getBoundaryQuadratureForScalarField(boundary); + I_dim = speye(obj.dim, obj.dim); + H = kron(H, I_dim); + end + + % Returns square boundary quadrature matrix, of dimension + % corresponding to the number of boundary grid points + % + % boundary -- string + function H_b = getBoundaryQuadratureForScalarField(obj, boundary) + assertIsMember(boundary, {'w', 'e', 's', 'n'}) + + H_b = obj.(['H_', boundary]); + end + + function N = size(obj) + N = (obj.dim + obj.dim^2)*prod(obj.m); + end + end +end