comparison +scheme/Schrodinger2d.m @ 1072:6468a5f6ec79 feature/grids/LaplaceSquared

Merge with default
author Jonatan Werpers <jonatan@werpers.com>
date Tue, 12 Feb 2019 17:12:42 +0100
parents 8d73fcdb07a5
children
comparison
equal deleted inserted replaced
1071:92cb03e64ca4 1072:6468a5f6ec79
1 classdef Schrodinger2d < scheme.Scheme
2
3 % Discretizes the Laplacian with constant coefficent,
4 % in the Schrödinger equation way (i.e., the discretization matrix is not necessarily
5 % definite)
6 % u_t = a*i*Laplace u
7 % opSet should be cell array of opSets, one per dimension. This
8 % is useful if we have periodic BC in one direction.
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 matrix for variable coefficients
20 a % Constant coefficient
21
22 D % Total operator
23 D1 % First derivatives
24
25 % Second derivatives
26 D2
27
28 H, Hi % Inner products
29 e_l, e_r
30 d1_l, d1_r % Normal derivatives at the boundary
31 e_w, e_e, e_s, e_n
32 d_w, d_e, d_s, d_n
33
34 H_boundary % Boundary inner products
35
36 end
37
38 methods
39
40 function obj = Schrodinger2d(g ,order, a, opSet)
41 default_arg('opSet',{@sbp.D2Variable, @sbp.D2Variable});
42 default_arg('a',1);
43 dim = 2;
44
45 assertType(g, 'grid.Cartesian');
46 if isa(a, 'function_handle')
47 a = grid.evalOn(g, a);
48 a = spdiag(a);
49 end
50
51 m = g.size();
52 m_tot = g.N();
53
54 h = g.scaling();
55 xlim = {g.x{1}(1), g.x{1}(end)};
56 ylim = {g.x{2}(1), g.x{2}(end)};
57 lim = {xlim, ylim};
58
59 % 1D operators
60 ops = cell(dim,1);
61 for i = 1:dim
62 ops{i} = opSet{i}(m(i), lim{i}, order);
63 end
64
65 I = cell(dim,1);
66 D1 = cell(dim,1);
67 D2 = cell(dim,1);
68 H = cell(dim,1);
69 Hi = cell(dim,1);
70 e_l = cell(dim,1);
71 e_r = cell(dim,1);
72 d1_l = cell(dim,1);
73 d1_r = cell(dim,1);
74
75 for i = 1:dim
76 I{i} = speye(m(i));
77 D1{i} = ops{i}.D1;
78 D2{i} = ops{i}.D2;
79 H{i} = ops{i}.H;
80 Hi{i} = ops{i}.HI;
81 e_l{i} = ops{i}.e_l;
82 e_r{i} = ops{i}.e_r;
83 d1_l{i} = ops{i}.d1_l;
84 d1_r{i} = ops{i}.d1_r;
85 end
86
87 % Constant coeff D2
88 for i = 1:dim
89 D2{i} = D2{i}(ones(m(i),1));
90 end
91
92 %====== Assemble full operators ========
93 obj.D1 = cell(dim,1);
94 obj.D2 = cell(dim,1);
95 obj.e_l = cell(dim,1);
96 obj.e_r = cell(dim,1);
97 obj.d1_l = cell(dim,1);
98 obj.d1_r = cell(dim,1);
99
100 % D1
101 obj.D1{1} = kron(D1{1},I{2});
102 obj.D1{2} = kron(I{1},D1{2});
103
104 % Boundary operators
105 obj.e_l{1} = kron(e_l{1},I{2});
106 obj.e_l{2} = kron(I{1},e_l{2});
107 obj.e_r{1} = kron(e_r{1},I{2});
108 obj.e_r{2} = kron(I{1},e_r{2});
109
110 obj.d1_l{1} = kron(d1_l{1},I{2});
111 obj.d1_l{2} = kron(I{1},d1_l{2});
112 obj.d1_r{1} = kron(d1_r{1},I{2});
113 obj.d1_r{2} = kron(I{1},d1_r{2});
114
115 % D2
116 obj.D2{1} = kron(D2{1},I{2});
117 obj.D2{2} = kron(I{1},D2{2});
118
119 % Quadratures
120 obj.H = kron(H{1},H{2});
121 obj.Hi = inv(obj.H);
122 obj.H_boundary = cell(dim,1);
123 obj.H_boundary{1} = H{2};
124 obj.H_boundary{2} = H{1};
125
126 % Differentiation matrix D (without SAT)
127 D2 = obj.D2;
128 D = sparse(m_tot,m_tot);
129 for j = 1:dim
130 D = D + a*1i*D2{j};
131 end
132 obj.D = D;
133 %=========================================%
134
135 % Misc.
136 obj.m = m;
137 obj.h = h;
138 obj.order = order;
139 obj.grid = g;
140 obj.dim = dim;
141 obj.a = a;
142 obj.e_w = obj.e_l{1};
143 obj.e_e = obj.e_r{1};
144 obj.e_s = obj.e_l{2};
145 obj.e_n = obj.e_r{2};
146 obj.d_w = obj.d1_l{1};
147 obj.d_e = obj.d1_r{1};
148 obj.d_s = obj.d1_l{2};
149 obj.d_n = obj.d1_r{2};
150
151 end
152
153
154 % Closure functions return the operators applied to the own domain to close the boundary
155 % Penalty functions return the operators to force the solution. In the case of an interface it returns the operator applied to the other doamin.
156 % boundary is a string specifying the boundary e.g. 'l','r' or 'e','w','n','s'.
157 % type is a string specifying the type of boundary condition.
158 % data is a function returning the data that should be applied at the boundary.
159 % neighbour_scheme is an instance of Scheme that should be interfaced to.
160 % neighbour_boundary is a string specifying which boundary to interface to.
161 function [closure, penalty] = boundary_condition(obj, boundary, type, parameter)
162 default_arg('type','Neumann');
163 default_arg('parameter', []);
164
165 % nj: outward unit normal component.
166 % nj = -1 for west, south, bottom boundaries
167 % nj = 1 for east, north, top boundaries
168 nj = obj.getBoundarySign(boundary);
169 [e, d] = obj.getBoundaryOperator({'e', 'd'}, boundary);
170 H_gamma = obj.getBoundaryQuadrature(boundary);
171 Hi = obj.Hi;
172 a = e'*obj.a*e;
173
174 switch type
175
176 % Dirichlet boundary condition
177 case {'D','d','dirichlet','Dirichlet'}
178 closure = nj*Hi*d*a*1i*H_gamma*(e' );
179 penalty = -nj*Hi*d*a*1i*H_gamma;
180
181 % Free boundary condition
182 case {'N','n','neumann','Neumann'}
183 closure = -nj*Hi*e*a*1i*H_gamma*(d' );
184 penalty = nj*Hi*e*a*1i*H_gamma;
185
186 % Unknown boundary condition
187 otherwise
188 error('No such boundary condition: type = %s',type);
189 end
190 end
191
192 % type Struct that specifies the interface coupling.
193 % Fields:
194 % -- interpolation: type of interpolation, default 'none'
195 function [closure, penalty] = interface(obj,boundary,neighbour_scheme,neighbour_boundary,type)
196
197 defaultType.interpolation = 'none';
198 default_struct('type', defaultType);
199
200 switch type.interpolation
201 case {'none', ''}
202 [closure, penalty] = interfaceStandard(obj,boundary,neighbour_scheme,neighbour_boundary,type);
203 case {'op','OP'}
204 [closure, penalty] = interfaceNonConforming(obj,boundary,neighbour_scheme,neighbour_boundary,type);
205 otherwise
206 error('Unknown type of interpolation: %s ', type.interpolation);
207 end
208 end
209
210 function [closure, penalty] = interfaceStandard(obj,boundary,neighbour_scheme,neighbour_boundary,type)
211 % u denotes the solution in the own domain
212 % v denotes the solution in the neighbour domain
213
214 % Get boundary operators
215 [e_v, d_v] = neighbour_scheme.getBoundaryOperator({'e', 'd'}, neighbour_boundary);
216 [e_u, d_u] = obj.getBoundaryOperator({'e', 'd'}, boundary);
217 H_gamma = obj.getBoundaryQuadrature(boundary);
218 Hi = obj.Hi;
219 a = obj.a;
220
221 % Get outward unit normal component
222 n = obj.getBoundarySign(boundary);
223
224 Hi = obj.Hi;
225 sigma = -n*1i*a/2;
226 tau = -n*(1i*a)'/2;
227
228 closure = tau*Hi*d*H_gamma*e' + sigma*Hi*e*H_gamma*d';
229 penalty = -tau*Hi*d*H_gamma*e_neighbour' ...
230 -sigma*Hi*e*H_gamma*d_neighbour';
231
232 end
233
234 function [closure, penalty] = interfaceNonConforming(obj,boundary,neighbour_scheme,neighbour_boundary,type)
235
236 % User can request special interpolation operators by specifying type.interpOpSet
237 default_field(type, 'interpOpSet', @sbp.InterpOpsOP);
238 interpOpSet = type.interpOpSet;
239
240 % u denotes the solution in the own domain
241 % v denotes the solution in the neighbour domain
242 [e_v, d_v] = neighbour_scheme.getBoundaryOperator({'e', 'd'}, neighbour_boundary);
243 [e_u, d_u] = obj.getBoundaryOperator({'e', 'd'}, boundary);
244 H_gamma = obj.getBoundaryQuadrature(boundary);
245 Hi = obj.Hi;
246 a = obj.a;
247
248 % Get outward unit normal component
249 n = obj.getBoundarySign(boundary);
250
251 % Find the number of grid points along the interface
252 m_u = size(e_u, 2);
253 m_v = size(e_v, 2);
254
255 % Build interpolation operators
256 intOps = interpOpSet(m_u, m_v, obj.order, neighbour_scheme.order);
257 Iu2v = intOps.Iu2v;
258 Iv2u = intOps.Iv2u;
259
260 sigma = -n*1i*a/2;
261 tau = -n*(1i*a)'/2;
262
263 closure = tau*Hi*d_u*H_gamma*e_u' + sigma*Hi*e_u*H_gamma*d_u';
264 penalty = -tau*Hi*d_u*H_gamma*Iv2u.good*e_v' ...
265 -sigma*Hi*e_u*H_gamma*Iv2u.bad*d_v';
266
267 end
268
269 % Returns the coordinate number and outward normal component for the boundary specified by the string boundary.
270 function [j, nj] = get_boundary_number(obj, boundary)
271
272 switch boundary
273 case {'w','W','west','West', 'e', 'E', 'east', 'East'}
274 j = 1;
275 case {'s','S','south','South', 'n', 'N', 'north', 'North'}
276 j = 2;
277 otherwise
278 error('No such boundary: boundary = %s',boundary);
279 end
280
281 switch boundary
282 case {'w','W','west','West','s','S','south','South'}
283 nj = -1;
284 case {'e', 'E', 'east', 'East','n', 'N', 'north', 'North'}
285 nj = 1;
286 end
287 end
288
289 % Returns the boundary operator op for the boundary specified by the string boundary.
290 % op -- string or a cell array of strings
291 % boundary -- string
292 function varargout = getBoundaryOperator(obj, op, boundary)
293 assertIsMember(boundary, {'w', 'e', 's', 'n'})
294
295 if ~iscell(op)
296 op = {op};
297 end
298
299 for i = 1:numel(op)
300 switch op{i}
301 case 'e'
302 switch boundary
303 case 'w'
304 e = obj.e_w;
305 case 'e'
306 e = obj.e_e;
307 case 's'
308 e = obj.e_s;
309 case 'n'
310 e = obj.e_n;
311 end
312 varargout{i} = e;
313
314 case 'd'
315 switch boundary
316 case 'w'
317 d = obj.d_w;
318 case 'e'
319 d = obj.d_e;
320 case 's'
321 d = obj.d_s;
322 case 'n'
323 d = obj.d_n;
324 end
325 varargout{i} = d;
326 end
327 end
328 end
329
330 % Returns square boundary quadrature matrix, of dimension
331 % corresponding to the number of boundary points
332 %
333 % boundary -- string
334 function H_b = getBoundaryQuadrature(obj, boundary)
335 assertIsMember(boundary, {'w', 'e', 's', 'n'})
336
337 switch boundary
338 case 'w'
339 H_b = obj.H_boundary{1};
340 case 'e'
341 H_b = obj.H_boundary{1};
342 case 's'
343 H_b = obj.H_boundary{2};
344 case 'n'
345 H_b = obj.H_boundary{2};
346 end
347 end
348
349 % Returns the boundary sign. The right boundary is considered the positive boundary
350 % boundary -- string
351 function s = getBoundarySign(obj, boundary)
352 assertIsMember(boundary, {'w', 'e', 's', 'n'})
353
354 switch boundary
355 case {'e','n'}
356 s = 1;
357 case {'w','s'}
358 s = -1;
359 end
360 end
361
362 function N = size(obj)
363 N = prod(obj.m);
364 end
365 end
366 end