-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathlapsolve.m
327 lines (291 loc) · 9.25 KB
/
lapsolve.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
function [phi,fd,fr,sidenum] = lapsolve(p,bdata)
%LAPSOLVE Solve Laplace's equation on a polygon.
%
% PHI = LAPSOLVE(P,BDATA) produces a solution to Laplace's equation
% with piecewise constant boundary conditions on a polygon P. The
% vector BDATA should have one entry for each side of P: A constant
% value indicates a Dirichlet condition, while NaN indicates a
% homogeneous Neumann condiiton.
%
% PHI = LAPSOLVE(P) opens a GUI for selecting the boundary
% conditions interactively.
%
% P may instead be an HPLMAP, in which case the target polygon of
% the map is used. This can save effort if multiple BVPs are to be
% solved on the same region.
%
% The output argument is a COMPOSITE function. The syntax PHI(z)
% should be used to compute PHI at points. It cannot be inverted or
% differentiated explicitly. To plot the solution you can use
%
% phi = lapsolve(p);
% [tri,x,y] = triangulate(p);
% trisurf(tri,x,y,phi(x+i*y))
%
% [PHI,FD,FR,SIDENUM] = LAPSOLVE(P,...) returns additional
% information. FD and FR are two Schwarz-Christoffel maps (of type
% DISKMAP and RIESURFMAP) such that
%
% -1
% phi(z) = real( fr( fd (z) ) ).
%
% The polygon Q=POLYGON(FR) is conformally equivalent to P but may
% have additional trivial vertices. SIDENUM helps with the
% identification. SIDENUM(k) is the shows which side of P that side
% k of Q lies on. For example, BDATA(SIDENUM) extends the boundary
% data to Q.
%
% See also DISKMAP, RIESURFMAP, COMPOSITE, POLYGON/TRIANGULATE.
import sctool.*
% First, map to half-plane if not done.
if isequal(class(p),'hplmap')
f = p;
p = polygon(f);
else
err = sccheck('hp',vertex(p),angle(p)-1);
if err
fprintf(['Polygon does not meet HPLMAP requirements. Use SCFIX first' ...
' and adjust boundary data accordingly.']);
error(' ');
end
f = hplmap(p);
end
% Get BC if not specified.
if nargin==1
bdata = lapsolvegui(p);
end
% Interpret Neumann and Dirichlet selections.
bdata = bdata(:);
n = length(bdata);
if n~=length(p)
error('Incorrect number of boundary conditions.')
end
neumann = isnan(bdata);
dirichlet = ~neumann;
% === Linear parameter problem ===
% Prevertices.
z = prevertex(f);
% Translate them to avoid zero.
z = z-3;
% Angles of target.
d = diff(dirichlet([n 1:n]));
alpha = 0.5 * (d~=0); % D/N or N/D transition
NN = (d==0) & neumann; % adjacent N sides
alpha(NN) = 1; % no turn
DD = (d==0) & (bdata([n 1:n-1])==bdata); % adj D sides with same value
alpha(DD) = 1;
dirichlet = dirichlet & ~DD;
diridx = find(dirichlet);
beta = alpha - 1;
% Index of problem.
kappa = -sum( beta );
if kappa < 2
error('At least two distinct Dirichlet values must be prescribed.')
end
% Endpoints of integration (points on the Dirichlet sides).
Z = [ z(1:n-1); 0; -10];
ze = 0.5*( Z(diridx) + Z(diridx+1) );
% Global rotation.
if neumann(n-1)
rho = 1;
else
rho = i;
end
% Integrate between neighboring midpoints.
zs = [ z(1:n-1); 0]; % includes generic monomial
bs = [ beta(1:n-1); 0];
M = zeros(kappa-1,kappa-1);
s = zeros(size(ze)); % no singular endpoints.
for d = 1:kappa-1
qdata = scqdata(bs,15);
M(:,d) = hplmap.hpquad(ze(1:end-1),ze(2:end),s,s,zs,bs,qdata);
bs(end) = bs(end) + 1; % next monomial
end
% Find the slit polynomial (high degree last).
dphi = diff( bdata(dirichlet) );
poly = real(rho*M) \ dphi;
% Slit and branch locations.
r = roots(flipud(poly));
% Roots in lower half-plane are redundant.
r(imag(r)<-1e-12) = [];
idx = imag(r) > 1e-12;
zb = r(idx);
slit = real(r(~idx));
% Final prevertices and angles.
ns = length(slit);
z = [ z; slit; ];
alpha = [ alpha; 2*ones(ns,1) ];
% Keep track of which original side was the "parent", so we know what BC
% is applied where. (To be filled in after sorting.)
sidenum = [ (1:n)'; NaN*ones(ns,1) ];
% Sort.
[z,idx] = sort(z);
alpha = alpha(idx);
sidenum = sidenum(idx);
N = length(z);
% === Construction of target region ===
% Insert "anchor" points after every infinite vertex.
% (More than is really needed, but helpful for various boring reasons.)
add = find( alpha(1:N-1)==0 );
dz = min(z(add+1),z(N-1)+2) - z(add);
new = z(add) + dz/3;
new = [new; z(add) + 2*dz/3];
% Don't let the first vertex be infinite.
if alpha(N)==0
new = [z(1)-2;z(1)-1;new];
end
z = [ z; new ];
alpha = [ alpha; ones(size(new)) ];
sidenum = [ sidenum; NaN*ones(size(new)) ];
% Sort again.
[z,idx] = sort(z);
alpha = alpha(idx); sidenum = sidenum(idx);
N = length(z);
% Find side numbers.
original = find(~isnan(sidenum));
sidenum(1:original(1)-1) = n;
for j = 1:n-1
sidenum( original(j)+1:original(j+1)-1 ) = j;
end
% Now that we have prevertices, transform to the disk since it's more
% convenient overall.
phi = moebius(z(N-2:N),[-1 -i 1]);
z = phi(z);
zb = phi(zb);
beta = alpha-1;
qdata = scqdata(beta,15);
% We need to determine the multiplicative constant, using a known
% difference in real parts (D values).
% First, find all finite D vertices.
Dvert = find(dirichlet(sidenum) & alpha~=0);
first = Dvert(1);
second = Dvert( 1 + min( find( diff( bdata(sidenum(Dvert)) ) ) ) );
% Use multiple versions and overdetermine for safety.
dt = angle( z(rem(first,N)+1)/z(first) );
z1 = z(first) * exp(i*dt*(0.2:0.2:0.8)');
dt = angle( z(rem(second,N)+1)/z(second) );
z2 = z(second) * exp(i*dt*(0.2:0.2:0.8)');
q = rsquad(z1,0*z1,0*z1,z,beta,zb,qdata) - ...
rsquad(z2,0*z2,0*z1,z,beta,zb,qdata);
dphi = diff( bdata( sidenum([first second]) ) );
C = [ real(q) -imag(q) ] \ (dphi*ones(4,1));
c = C(1) + 1i*C(2);
% Start the target with known infinities.
w = NaN*ones(N,1);
w(alpha==0) = Inf;
finite = find( alpha > 0 );
% Integrate to find the finite ones, relative to the first.
idxl = finite(1:end-1)'; idxr = finite(2:end)';
zl = z(idxl); zr = z(idxr);
midpt = 0*zl;
q1 = rsquad(zl,midpt,idxl,z,beta,zb,qdata);
q2 = rsquad(zr,midpt,idxr,z,beta,zb,qdata);
w(finite) = c*cumsum([0;q1-q2]);
% Translate to correct first Dirichlet value.
w = w - w(first) + bdata(sidenum(first));
Q = polygon(w,alpha);
% Find branch points.
z1 = repmat(z(1),[length(zb) 1]);
branch = w(1) + c*rsquad(z1,zb,ones(size(z1)),z,beta,zb,qdata);
% Create output objects.
fd = diskmap(p,z(original));
if isempty(zb)
fr = diskmap(Q,z,c);
else
fr = riesurfmap(Q,branch,z,zb,c);
end
phi = composite( inv(fd), fr, inline('real(z)') );
% === Needed for RIESURFMAP quadrature. ===
function I = rsquad(z1,z2,varargin)
%RSQUAD (not intended for calling directly by the user)
% Numerical quadrature for the Riemann surface map.
% Copyright 2002 by Toby Driscoll.
% $Id: lapsolve.m 298 2009-09-15 14:36:37Z driscoll $
% RSQUAD(z1,z2,sing1,z,beta,zb,qdat)
%
% z1,z2 are vectors of left and right endpoints. sing1 is a vector of
% integer indices which label the singularities in z1. So if sing1(5)
% = 3, then z1(5) = z(3). A zero means no singularity. z is the
% vector of singularities; beta is the vector of associated turning
% angles. qdat is quadrature data from SCQDATA.
%
% Make sure that z and beta are column vectors.
%
% The integral is subdivided, if necessary, so that no singularity
% lies closer to the left endpoint than 1/2 the length of the
% integration (sub)interval.
%
% RSQUAD(z1,z2,sing1,sing2,z,beta,zb,qdat)
%
% Integrate from one singularity to another. Picks the origin as the
% midpoint of integration; this may NOT be wise if the path goes near a
% branch point.
if nargin==8
% Break into two pieces with recursive call.
[sing1,sing2,z,beta,zb,qdat] = deal(varargin{:});
mid = zeros(size(z1));
I1 = rsquad(z1,mid,sing1,z,beta,qdat);
I2 = rsquad(z2,mid,sing2,z,beta,qdat);
I = I1-I2;
return
else
[sing1,z,beta,zb,qdat] = deal(varargin{:});
end
nqpts = size(qdat,1);
n = length(z);
bigz = z(:,ones(1,nqpts));
bigbeta = beta(:,ones(1,nqpts));
if isempty(sing1)
sing1 = zeros(length(z1),1);
end
B = length(zb);
if B>0
bigbranch = zb(:,ones(1,nqpts));
else
bigbranch = zeros(1,nqpts);
end
I = zeros(size(z1));
nontriv = find(z1(:)~=z2(:))';
for k = nontriv
za = z1(k);
zb = z2(k);
sng = sing1(k);
% Allowable integration step, based on nearest singularity.
dist = min(1,2*min(abs(z([1:sng-1,sng+1:n])-za))/abs(zb-za));
zr = za + dist*(zb-za);
% Adjust Gauss-Jacobi nodes and weights to interval.
ind = rem(sng+n,n+1)+1;
nd = ((zr-za)*qdat(:,ind) + zr + za).'/2; % G-J nodes
wt = ((zr-za)/2) * qdat(:,ind+n+1); % G-J weights
terms = 1 - (nd(ones(n,1),:))./bigz;
if any(terms(:)==0)
% Endpoints are practically coincident.
I(k) = 0;
else
% Use Gauss-Jacobi on first subinterval, if necessary.
if sng > 0
terms(sng,:) = terms(sng,:)./abs(terms(sng,:));
wt = wt*(abs(zr-za)/2)^beta(sng);
end
Q = exp(sum(log(terms).*bigbeta));
if B > 0
ND = nd(ones(B,1),:);
Q = Q.*prod((ND-bigbranch).*(1-ND.*conj(bigbranch)),1);
end
I(k) = Q*wt;
while dist < 1
% Do regular Gaussian quad on other subintervals.
zl = zr;
dist = min(1,2*min(abs(z-zl))/abs(zl-zb));
zr = zl + dist*(zb-zl);
nd = ((zr-zl)*qdat(:,n+1) + zr + zl).'/2;
wt = ((zr-zl)/2) * qdat(:,2*n+2);
Q = exp(sum(log(1 - nd(ones(n,1),:)./bigz).*bigbeta));
if B > 0
ND = nd(ones(B,1),:);
Q = Q.*prod((ND-bigbranch).*(1-ND.*conj(bigbranch)),1);
end
I(k) = I(k) + Q*wt;
end
end
end