Skip to content

Commit 3fe062e

Browse files
authored
Merge pull request #105 from haiszhu/dev-helmkernsplit-redo
laplace and helmholtz kernel split quadrature
2 parents 57807af + b7e0433 commit 3fe062e

File tree

9 files changed

+456
-55
lines changed

9 files changed

+456
-55
lines changed

chunkie/+chnk/pquadwts.m

Lines changed: 124 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,78 @@
1-
function mat = pquadwts(r,d,n,d2,ct,bw,j,...
2-
rt,dt,nt,d2t,kern,opdims,t,w,opts,intp_ab,intp)
3-
%CHNK.INTERPQUADWTS product integration for interaction of kernel on chunk
1+
function [varargout] = pquadwts(r,d,n,d2,wts,j,...
2+
rt,t,w,opts,intp_ab,intp,types)
3+
%CHNK.pquadwts product integration for interaction of kernel on chunk
44
% at targets
55
%
66
% WARNING: this routine is not designed to be user-callable and assumes
77
% a lot of precomputed values as input
88
%
9-
% Syntax: mat = interpquadwts(r,d,d2,h,ct,bw,j, ...
10-
% rt,dt,d2t,kern,opdims,t,w,opts)
9+
% Syntax: [varargout] = pquadwts(r,d,n,d2,wts,j,...
10+
% rt,t,w,opts,intp_ab,intp,types)
1111
%
1212
% Input:
1313
% r - chnkr nodes
1414
% d - chnkr derivatives at nodes
1515
% n - chnkr normals at nodes
1616
% d2 - chnkr 2nd derivatives at nodes
17-
% ct - Legendre nodes at order of chunker
18-
% bw - barycentric interpolation weights for Legendre nodes at order of
19-
% chunker
17+
% wts - chnkr integration weights for smooth functions
2018
% j - chunk of interest
21-
% rt,dt,nt,d2t - position, derivative, normal,second derivative of select
22-
% target points. if any are not used by kernel (or not well
23-
% defined, e.g. when not on curve), a dummy array
24-
% of the appropriate size should be supplied
25-
% kern - kernel function of form kern(srcinfo,targinfo)
26-
% opdims - dimensions of kernel
27-
% t - (Legendre) integration nodes for adaptive integration
28-
% w - integration nodes for adaptive integrator (t and w not necessarily
29-
% same order as chunker order)
19+
% rt - position of target points. if any are not used by kernel
20+
% (or not well defined, e.g. when not on curve), a
21+
% dummy array of the appropriate size should be supplied
22+
% t - (Legendre) integration nodes
23+
% w - (Legendre) integration weights
24+
% opts - opts.side
25+
% intp_ab - panel endpoints interpolation matrix
26+
% types - specified singularity types
27+
% [0 0 0 0] - smooth quadr
28+
% [1 0 0 0] - log singularity
29+
% [0 0 -1 0] - cauchy singularity
30+
% each [a, b, c, d]
31+
% corresponds to (log(z-w))^a (log(zc-wc))^b (z-w)^c (zc-wc)^d
3032
%
3133
% Output
32-
% mat - integration matrix
34+
% varargout - integration matrices for specified singularity types
3335
%
3436

3537
% Helsing-Ojala (interior/exterior?)
36-
3738
xlohi = intp_ab*(r(1,:,j)'+1i*r(2,:,j)'); % panel end points
3839
r_i = intp*(r(1,:,j)'+1i*r(2,:,j)'); % upsampled panel
39-
d_i = (intp*(d(1,:,j)'+1i*d(2,:,j)')); % r'
40-
d2_i = (intp*(d2(1,:,j)'+1i*d2(2,:,j)')); % r''
40+
d_i = (intp*(d(1,:,j)'+1i*d(2,:,j)')); % r'
41+
d2_i = (intp*(d2(1,:,j)'+1i*d2(2,:,j)')); % r''
42+
wts_i = wts(:,j)'; %
4143
sp = abs(d_i); tang = d_i./sp; % speed, tangent
4244
n_i = -1i*tang; % normal
4345
cur = -real(conj(d2_i).*n_i)./sp.^2; % curvature
44-
wxp_i = w.*d_i; % complex speed weights (Helsing's wzp)
46+
wxp_i = w.*d_i; % complex speed weights (Helsing's wzp)
47+
48+
varargout = cell(size(types));
49+
50+
% [As, Ad, A1, A2, A3, A4] = SDspecialquad(struct('x',rt(1,:)' + 1i*rt(2,:)'),...
51+
% struct('x',r_i,'nx',n_i,'wxp',wxp_i),xlohi(1),xlohi(2),opts.side);
52+
[As, Ad] = SDspecialquad(struct('x',rt(1,:)' + 1i*rt(2,:)'),...
53+
struct('x',r_i,'nx',n_i,'wxp',wxp_i),xlohi(1),xlohi(2),opts.side);
54+
As = As*intp;
55+
Ad = Ad*intp;
4556

46-
mat_ho_slp = Sspecialquad(struct('x',rt(1,:)' + 1i*rt(2,:)'),...
47-
struct('x',r_i,'nx',n_i,'wxp',wxp_i),xlohi(1),xlohi(2),'e');
48-
mat_ho_dlp = Dspecialquad(struct('x',rt(1,:)' + 1i*rt(2,:)'),...
49-
struct('x',r_i,'nx',n_i,'wxp',wxp_i),xlohi(1),xlohi(2),'e');
57+
for j = 1:length(types)
58+
type0 = types{j};
5059

51-
mat = (mat_ho_slp+real(mat_ho_dlp))*intp; % depends on kern, different mat1?
60+
if (all(type0 == [0 0 0 0]))
61+
varargout{j} = ones(size(rt,2),numel(wts_i)).*wts_i;
62+
elseif (all(type0 == [1 0 0 0]))
63+
% varargout{j} = Sspecialquad(struct('x',rt(1,:)' + 1i*rt(2,:)'),...
64+
% struct('x',r_i,'nx',n_i,'wxp',wxp_i),xlohi(1),xlohi(2),opts.side)*intp;
65+
varargout{j} = As;
66+
elseif (all(type0 == [0 0 -1 0]))
67+
% varargout{j} = Dspecialquad(struct('x',rt(1,:)' + 1i*rt(2,:)'),...
68+
% struct('x',r_i,'nx',n_i,'wxp',wxp_i),xlohi(1),xlohi(2),opts.side)*intp;
69+
varargout{j} = Ad;
70+
else
71+
error("split panel quad type " + join(string([1 2 3]),",") + " not available");
72+
end
5273

5374
end
75+
end
5476

5577
function [A, A1, A2] = Sspecialquad(t,s,a,b,side)
5678
% SSPECIALQUAD - SLP val+grad close-eval Helsing "special quadrature" matrix
@@ -66,6 +88,7 @@
6688
% Efficient only if multiple targs, since O(p^3).
6789
% See Helsing-Ojala 2008 (special quadr Sec 5.1-2), Helsing 2009 mixed (p=16),
6890
% and Helsing's tutorial demo11b.m LGIcompRecFAS()
91+
% See https://github.com/ahbarnett/BIE2D/blob/master/panels/LapSLP_closepanel.m
6992
if nargin<5, side = 'i'; end % interior or exterior
7093
zsc = (b-a)/2; zmid = (b+a)/2; % rescaling factor and midpoint of src segment
7194
y = (s.x-zmid)/zsc; x = (t.x-zmid)/zsc; % transformed src nodes, targ pts
@@ -146,6 +169,7 @@
146169
% Efficient only if multiple targs, since O(p^3).
147170
% See Helsing-Ojala 2008 (special quadr Sec 5.1-2), Helsing 2009 mixed (p=16),
148171
% and Helsing's tutorial demo11b.m M1IcompRecFS()
172+
% See https://github.com/ahbarnett/BIE2D/blob/master/panels/LapDLP_closepanel.m
149173
if nargin<5, side = 'i'; end % interior or exterior
150174
zsc = (b-a)/2; zmid = (b+a)/2; % rescaling factor and midpoint of src segment
151175
y = (s.x-zmid)/zsc; x = (t.x-zmid)/zsc; % transformed src nodes, targ pts
@@ -203,3 +227,76 @@
203227
end
204228
end
205229
end
230+
231+
function [As, A, A1, A2, A3, A4] = SDspecialquad(t,s,a,b,side)
232+
% S+D together... (with Sn) should be the same as requesting S or D
233+
% more expensive if request Dn...
234+
% https://github.com/ahbarnett/BIE2D/blob/master/panels/LapDLP_closepanel.m
235+
%%% d = 100;
236+
zsc = (b-a)/2; zmid = (b+a)/2; % rescaling factor and midpoint of src segment
237+
y = (s.x-zmid)/zsc; x = (t.x-zmid)/zsc; % transformed src nodes, targ pts
238+
%figure; plot(x,'.'); hold on; plot(y,'+-'); plot([-1 1],[0 0],'ro'); % debug
239+
N = numel(x); % # of targets
240+
p = numel(s.x); % assume panel order is # nodes
241+
if N*p==0
242+
As = 0; A = 0; A1=0; A2=0;
243+
return
244+
end
245+
c = (1-(-1).^(1:p))./(1:p); % Helsing c_k, k = 1..p.
246+
V = ones(p,p); for k=2:p, V(:,k) = V(:,k-1).*y; end % Vandermonde mat @ nodes
247+
P = zeros(p+1,N); % Build P, Helsing's p_k vectorized on all targs...
248+
d = 1.1; inr = abs(x)<=d; ifr = abs(x)>d; % near & far treat separately
249+
%gam = 1i;
250+
gam = exp(1i*pi/4); % smaller makes cut closer to panel. barnett 4/17/18
251+
if side == 'e', gam = conj(gam); end % note gam is a phase, rots branch cut
252+
P(1,:) = log(gam) + log((1-x)./(gam*(-1-x))); % init p_1 for all targs int
253+
254+
% upwards recurrence for near targets, faster + more acc than quadr...
255+
% (note rotation of cut in log to -Im; so cut in x space is lower unit circle)
256+
if N>1 || (N==1 && inr==1) % Criterion added by Bowei Wu 03/05/15 to ensure inr not empty
257+
for k=1:p
258+
P(k+1,inr) = x(inr).'.*P(k,inr) + c(k);
259+
end % recursion for p_k
260+
end
261+
% fine quadr (no recurrence) for far targets (too inaccurate cf downwards)...
262+
Nf = numel(find(ifr)); wxp = s.wxp/zsc; % rescaled complex speed weights
263+
if Nf>0 % Criterion added by Bowei Wu 03/05/15 to ensure ifr not empty
264+
P(end,ifr) = sum(((wxp.*(V(:,end).*y(:)))*ones(1,Nf))./bsxfun(@minus,y,x(ifr).')); % int y^p/(y-x)
265+
for ii = p:-1:2
266+
P( ii,ifr) = (P(ii+1,ifr)-c(ii))./x(ifr).';
267+
end
268+
end
269+
270+
Q = zeros(p,N); % compute q_k from p_k via Helsing 2009 eqn (18)... (p even!)
271+
% Note a rot ang appears here too... 4/17/18
272+
%gam = exp(1i*pi/4); % 1i; % moves a branch arc as in p_1
273+
%if side == 'e', gam = conj(gam); end % note gam is a phase, rots branch cut
274+
Q(1:2:end,:) = P(2:2:end,:) - repmat(log((1-x.').*(-1-x.')),[ceil(p/2) 1]); % guessed!
275+
% (-1)^k, k odd, note each log has branch cut in semicircle from -1 to 1
276+
% Q(2:2:end,:) = P(3:2:end,:) - repmat(log((1-x.')./((-1-x.'))),[floor(p/2) 1]); % same cut as for p_1
277+
Q(2:2:end,:) = P(3:2:end,:) - repmat(log(gam) + log((1-x.')./(gam*(-1-x.'))),[floor(p/2) 1]); % same cut as for p_1
278+
Q = Q.*repmat(1./(1:p)',[1 N]); % k even
279+
As = real((V.'\Q).'.*repmat((1i*s.nx)',[N 1])*zsc)/(2*pi*abs(zsc));
280+
As = As*abs(zsc) - log(abs(zsc))/(2*pi)*repmat(abs(s.wxp)',[N 1]); % unscale, yuk
281+
warning('off','MATLAB:nearlySingularMatrix');
282+
% A = real((V.'\P).'*(1i/(2*pi))); % solve for special quadr weights
283+
A = ((V.'\P(1:p,:)).'*(1i/(2*pi))); % do not take real for the eval of Stokes DLP non-laplace term. Bowei 10/19/14
284+
%A = (P.'*inv(V))*(1i/(2*pi)); % equiv in exact arith, but not bkw stable.
285+
if nargout>2
286+
R = -(kron(ones(p,1),1./(1-x.')) + kron((-1).^(0:p-1).',1./(1+x.'))) +...
287+
repmat((0:p-1)',[1 N]).*[zeros(1,N); P(1:p-1,:)]; % hypersingular kernel weights of Helsing 2009 eqn (14)
288+
Az = (V.'\R).'*(1i/(2*pi*zsc)); % solve for targ complex-deriv mat & rescale
289+
A1 = Az;
290+
if nargout > 3
291+
S = -(kron(ones(p,1),1./(1-x.').^2) - kron((-1).^(0:p-1).',1./(1+x.').^2))/2 +...
292+
repmat((0:p-1)',[1 N]).*[zeros(1,N); R(1:p-1,:)]/2; % supersingular kernel weights
293+
Azz = (V.'\S).'*(1i/(2*pi*zsc^2));
294+
if nargout > 4
295+
A1 = real(Az); A2 = -imag(Az); % note sign for y-deriv from C-deriv
296+
A3 = real(Azz); A4 = -imag(Azz);
297+
else
298+
A1 = Az; A2 = Azz;
299+
end
300+
end
301+
end
302+
end

chunkie/@kernel/helm2d.m

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,20 @@
4040
obj.eval = @(s,t) chnk.helm2d.kern(zk, s, t, 's');
4141
obj.fmm = @(eps,s,t,sigma) chnk.helm2d.fmm(eps, zk, s, t, 's', sigma);
4242
obj.sing = 'log';
43+
obj.splitinfo = [];
44+
obj.splitinfo.type = {[0 0 0 0],[1 0 0 0]};
45+
obj.splitinfo.action = {'r','r'};
46+
obj.splitinfo.functions = @(s,t) helm2d_s_split(zk,s,t);
4347

4448
case {'d', 'double'}
4549
obj.type = 'd';
4650
obj.eval = @(s,t) chnk.helm2d.kern(zk, s, t, 'd');
4751
obj.fmm = @(eps,s,t,sigma) chnk.helm2d.fmm(eps, zk, s, t, 'd', sigma);
4852
obj.sing = 'log';
53+
obj.splitinfo = [];
54+
obj.splitinfo.type = {[0 0 0 0],[1 0 0 0],[0 0 -1 0]};
55+
obj.splitinfo.action = {'r','r','r'};
56+
obj.splitinfo.functions = @(s,t) helm2d_d_split(zk,s,t);
4957

5058
case {'sp', 'sprime'}
5159
obj.type = 'sp';
@@ -69,6 +77,10 @@
6977
obj.eval = @(s,t) chnk.helm2d.kern(zk, s, t, 'c', coefs);
7078
obj.fmm = @(eps,s,t,sigma) chnk.helm2d.fmm(eps, zk, s, t, 'c', sigma, coefs);
7179
obj.sing = 'log';
80+
obj.splitinfo = [];
81+
obj.splitinfo.type = {[0 0 0 0],[1 0 0 0],[0 0 -1 0]};
82+
obj.splitinfo.action = {'r','r','r'};
83+
obj.splitinfo.functions = @(s,t) helm2d_c_split(zk,s,t,coefs);
7284

7385
case {'cp', 'cprime'}
7486
if ( nargin < 3 )
@@ -93,3 +105,37 @@
93105

94106

95107
end
108+
109+
function f = helm2d_s_split(zk,s,t)
110+
seval = chnk.helm2d.kern(zk, s, t, 's');
111+
dist = (s.r(1,:)+1i*s.r(2,:))-(t.r(1,:)'+1i*t.r(2,:)');
112+
logeval = log(abs(dist));
113+
f = cell(2,1);
114+
f{1} = seval + 1/(2*pi)*4*logeval.*imag(seval);
115+
f{2} = 4*imag(seval);
116+
end
117+
118+
function f = helm2d_d_split(zk,s,t)
119+
deval = chnk.helm2d.kern(zk, s, t, 'd');
120+
dist = (s.r(1,:)+1i*s.r(2,:))-(t.r(1,:)'+1i*t.r(2,:)');
121+
logeval = log(abs(dist));
122+
cauchyeval = (s.n(1,:)+1i*s.n(2,:))./(dist);
123+
f = cell(3, 1);
124+
f{1} = deval + 1/(2*pi)*4*logeval.*imag(deval) + 1/(2*pi)*real(cauchyeval);
125+
f{2} = 4*imag(deval);
126+
f{3} = ones(size(t.r,2),size(s.r,2));
127+
end
128+
129+
function f = helm2d_c_split(zk,s,t,coefs)
130+
seval = chnk.helm2d.kern(zk, s, t, 's');
131+
deval = chnk.helm2d.kern(zk, s, t, 'd');
132+
dist = (s.r(1,:)+1i*s.r(2,:))-(t.r(1,:)'+1i*t.r(2,:)');
133+
logeval = log(abs(dist));
134+
cauchyeval = (s.n(1,:)+1i*s.n(2,:))./(dist);
135+
f = cell(3, 1);
136+
f{1} = coefs(1) * (deval + 2/pi*logeval.*imag(deval) + 1/(2*pi)*real(cauchyeval)) + ...
137+
coefs(2) * (seval + 2/pi*logeval.*imag(seval));
138+
f{2} = coefs(1) * (4*imag(deval)) + coefs(2) * (4*imag(seval));
139+
f{3} = coefs(1)*ones(size(t.r,2),size(s.r,2));
140+
end
141+

chunkie/@kernel/lap2d.m

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,20 @@
4242
obj.eval = @(s,t) chnk.lap2d.kern(s, t, 's');
4343
obj.fmm = @(eps,s,t,sigma) chnk.lap2d.fmm(eps, s, t, 's', sigma);
4444
obj.sing = 'log';
45+
obj.splitinfo = [];
46+
obj.splitinfo.type = {[1 0 0 0]};
47+
obj.splitinfo.action = {'r'};
48+
obj.splitinfo.functions = @(s,t) lap2d_s_split(s,t);
4549

4650
case {'d', 'double'}
4751
obj.type = 'd';
4852
obj.eval = @(s,t) chnk.lap2d.kern(s, t, 'd');
4953
obj.fmm = @(eps,s,t,sigma) chnk.lap2d.fmm(eps, s, t, 'd', sigma);
5054
obj.sing = 'smooth';
55+
obj.splitinfo = [];
56+
obj.splitinfo.type = {[0 0 -1 0]};
57+
obj.splitinfo.action = {'r'};
58+
obj.splitinfo.functions = @(s,t) lap2d_d_split(s,t);
5159

5260
case {'sp', 'sprime'}
5361
obj.type = 'sp';
@@ -91,6 +99,10 @@
9199
obj.eval = @(s,t) chnk.lap2d.kern(s, t, 'c', coefs);
92100
obj.fmm = @(eps,s,t,sigma) chnk.lap2d.fmm(eps, s, t, 'c', sigma, coefs);
93101
obj.sing = 'log';
102+
obj.splitinfo = [];
103+
obj.splitinfo.type = {[1 0 0 0],[0 0 -1 0]};
104+
obj.splitinfo.action = {'r','r'};
105+
obj.splitinfo.functions = @(s,t) lap2d_c_split(s,t,coefs);
94106

95107
otherwise
96108
error('Unknown Laplace kernel type ''%s''.', type);
@@ -103,3 +115,20 @@
103115
end
104116

105117
end
118+
119+
function f = lap2d_s_split(s,t)
120+
f = cell(1,1);
121+
f{1} = ones(size(t.r,2),size(s.r,2));
122+
end
123+
124+
function f = lap2d_d_split(s,t)
125+
f = cell(1,1);
126+
f{1} = ones(size(t.r,2),size(s.r,2));
127+
end
128+
129+
function f = lap2d_c_split(s,t,coefs)
130+
f = cell(2,1);
131+
f{1} = coefs(2)*ones(size(t.r,2),size(s.r,2));
132+
f{2} = coefs(1)*ones(size(t.r,2),size(s.r,2));
133+
end
134+

chunkie/@kernel/stok2d.m

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@
7878
obj.fmm = @(eps, s, t, sigma) chnk.stok2d.fmm(eps, mu, s, t, 'svel', sigma);
7979
obj.opdims = [2, 2];
8080
obj.sing = 'log';
81+
obj.splitinfo = [];
82+
obj.splitinfo.type = {[1 0 0 0],[0 0 -1 0],[0 0 -1 0]};
83+
obj.splitinfo.action = {'r','r','i'};
84+
obj.splitinfo.functions = @(s,t) stok2d_s_split(mu, s, t);
8185

8286
case {'spres', 'spressure'}
8387
obj.type = 'spres';
@@ -188,3 +192,27 @@
188192

189193

190194
end
195+
196+
function f = stok2d_s_split(mu,s,t)
197+
dist = (s.r(1,:)+1i*s.r(2,:))-(t.r(1,:)'+1i*t.r(2,:)');
198+
distr = real(dist);
199+
disti = imag(dist);
200+
f = cell(3, 1);
201+
ntarg = numel(t.r(1,:));
202+
nsrc = numel(s.r(1,:));
203+
sn1mat = repmat(s.n(1,:),[ntarg 1]);
204+
sn2mat = repmat(s.n(2,:),[ntarg 1]);
205+
f{1} = zeros(2*ntarg,2*nsrc);
206+
f{2} = zeros(2*ntarg,2*nsrc);
207+
f{3} = zeros(2*ntarg,2*nsrc);
208+
f{1}(1:2:end,1:2:end) = 1/(2*mu);
209+
f{1}(2:2:end,2:2:end) = 1/(2*mu);
210+
f{2}(1:2:end,1:2:end) = -sn1mat.*distr/(2*mu);
211+
f{2}(1:2:end,2:2:end) = -sn2mat.*distr/(2*mu);
212+
f{2}(2:2:end,1:2:end) = -sn1mat.*disti/(2*mu);
213+
f{2}(2:2:end,2:2:end) = -sn2mat.*disti/(2*mu);
214+
f{3}(1:2:end,1:2:end) = -sn2mat.*distr/(2*mu);
215+
f{3}(1:2:end,2:2:end) = sn1mat.*distr/(2*mu);
216+
f{3}(2:2:end,1:2:end) = -sn2mat.*disti/(2*mu);
217+
f{3}(2:2:end,2:2:end) = sn1mat.*disti/(2*mu);
218+
end

0 commit comments

Comments
 (0)