forked from csdms-contrib/slepian_alpha
-
Notifications
You must be signed in to change notification settings - Fork 0
/
galpha.m
294 lines (274 loc) · 10.8 KB
/
galpha.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
function varargout=...
galpha(TH,L,sord,theta,phi,srt,upco,resc,blox,J,irr,Glma,V,N,EL,EM)
% [G,V,EM,GK,VK,NA,N,theta,phi,Glma,EL]=...
% GALPHA(TH,L,sord,theta,phi,srt,upco,resc,blox,J,irr)
%
% Constructs a matrix of axisymmetric-polar-cap Slepian eigenfunctions
% evaluated at a set of spatial locations. Normalization is to UNITY over
% the surface of the entire sphere.
%
% INPUT:
%
% TH Angular extent of the spherical cap, in degrees
% L Bandwidth (maximum angular degree) or passband (two degrees)
% sord 1 Single polar cap of radius TH [default]
% 2 Double polar cap, each of radius TH
% 3 Equatorial belt of width 2TH
% theta colatitude (0 <= theta <= pi) [default: 720 linearly spaced]
% phi longitude (0 <= theta <= 2*pi) [default: 0], if NaN, get Xlm
% srt 'global' Global sorting of eigenvalues across all orders
% 'local' Sorting of cap eigenvalues within orders [default]
% 'belt' Global sorting of eigenvalues on the belt
% upco +ve fraction of unit radius for upward continuation [default: 0]
% -ve fraction of unit radius for downward continuation
% resc 0 Not rescaled [default, unit-normalization is in effect]
% 1 Rescaled to retain unit integral over the unit sphere
% (this is only relevant for the down/upward continued functions)
% blox 1 or 0 but should not affect any output (see 'demo1')
% J How many of the best eigenfunctions do you want? [default: all]
% irr 0 Regular grid, no matter how you input theta and phi [default]
% 1 Irregular grid, input interpreted as distinct pairs of theta, phi
% Glma The spectral eigenfunctions in case you already have them
% V The spectral eigenvalues in case you already have them
% N The Shannon number in case you already have it
% EL The degrees in question if you already have them
% EM The orders in question if you already have them
%
% OUTPUT:
%
% G A matrix with dimensions
% (L+1)^2 x (length(theta)*length(phi))
% V A vector with the eigenvalues
% EM If axisymmetric, the order on which the taper is based
% (otherwise, this makes no sense and all orders are involved)
% NA N/A, which must be equal to diag(G'*G)
% N N, the Shannon number, rounded to the nearest integer
% GK Matrix G truncated to Shannon-number proportions
% VK Vector V truncated to Shannon-number proportions
% theta The colatitudes that you put in or received
% phi The longitudes that you put in or received
% Glma The spectral eigenfunctions, for use in, e.g. PLOTSLEP
% EL The degrees in question
%
% EXAMPLE:
%
% galpha('demo1') % should return no error messages
% galpha('demo2') % makes some quick plots at the Greenwich meridian
% galpha('demo3') % makes some quick 2D plots, checks normalization
% galpha('demo4') % makes some quick 2D plots, with a rotation
%
% G=galpha(40,18,1,linspace(0,pi,180),linspace(0,2*pi,360),'global');
%
% See also DOUBLECAP
%
% Last modified by fjsimons-at-alum.mit.edu, 07/11/2012
% If the output was on a Driscoll-Healey or HEALPIX grid (and maybe I
% should think about doing that), this would be an orthogonal matrix, but
% now it isn't, of course.... Should adapt for irregular grids as GALPHAPTO.
% Note to self:
%
% To find how many caps at different orders m are required for a total
% number of well concentrated function N, you could either run the
% fixed-order problem, and take the first round(Nm) of each of those,
% knowing that you have to take m as well as -m. But, although
% N0+2*sum(N1...NL) equals N exactly, it is not true that round(N) equals
% round(N0+2*sum(N1...NL))... So you might be making a small mistake if
% you go via route 1 above. Instead, you could run this code
% [G,V,EM,GK,VK,NA,N]=galpha(TH,L,1,[],NaN,[],'global');
% and figure out which orders to take by looking at EM(1:N)... the only
% trouble there, then, is, that you might be splitting a multiplet +/- m,
% and it will be up to you to decide whether or not to take another one
% or one fewer... Seems like the round(Nm) approach after all would be OK.
defval('TH',40)
if ~isstr(TH)
defval('L',18)
defval('sord',1)
defval('theta',linspace(0,pi,720))
defval('phi',0)
defval('srt','local')
defval('upco',0)
defval('resc',0)
% Note: the block sorting only matters in here and not for the output
defval('blox',0)
defval('irr',0)
% Basic error checks
if irr==1 & ~all(size(theta)==size(phi))
error('Input arrays must have the same dimensions for irregular grids')
end
if isempty(strcmp(lower(srt),'global')) & ...
isempty(strcmp(lower(srt),'local'))& ...
isempty(strcmp(lower(srt),'belt'))
error('Specify valid option ''srt''')
end
% Figure out if it's bandlimited or bandpass
lp=length(L)==1;
bp=length(L)==2;
maxL=max(L);
% The spherical-harmonic dimension
ldim=(L(2-lp)+1)^2-bp*L(1)^2;
% Adjust the calling sequence, then revert
if sord==3
sordo=2; THo=90-TH;
else
sordo=sord; THo=TH;
end
% Get the coefficients and the orders etc
% The EM's are for the tapers! the EMrows disappear later
if exist('Glma')~=1 || exist('V')~=1 || exist('N')~=1 ...
|| exist('EL')~=1 || exist('EM')~=1
[Glma,V,EL,EMrow,N,GAL,EM]=glmalpha(THo,L,sordo,blox,upco,resc);
end
% Calculate N/A and rounded Shannon number
NA=ldim/(4*pi);
% Slight quirk in the spharea function
if sord==1 || sord==3
N=round(ldim*spharea(TH,sord));
elseif sord==2
N=round(ldim*spharea(90-TH,sord));
end
% I suppose here later we can put the geographical regions,
% but, the lon/lat parameterization here in GALPHA is not ideal.
% Get the unit-normalized real spherical harmonics
if length(phi)==1 && isnan(phi)
XYlmr=xlm([0 maxL],[],theta,0,0,blox);
else
XYlmr=ylm([0 maxL],[],theta,phi,0,0,blox,irr);
end
% I believe this should get the (-1)^m in there also because if not it
% won't conform to how PLM2XYZ would do it should you use GLM2LMCOSI...
% But this can wait - not a critical function. GALPHAPTO already does
% it.
% Default truncation is none at all, so the 'srt' option has power
defval('J',ldim)
% Eigenvalue sorting? If 'local' doesn't do anything
if strcmp(lower(srt),'belt') || sord==3
V=1-V;
end
if strcmp(lower(srt),'global') || strcmp(lower(srt),'belt')
[V,i]=sort(V,'descend');
Glma=Glma(:,i); EM=EM(i);
end
% Truncation? If past Shannon number, do it from here on, if not, wait
% But doing the spectral truncation before the spatial expansion saves time
if J~=ldim && J>=N
Glma=Glma(:,1:J);
elseif J~=ldim && J<N
Glma=Glma(:,1:N);
end
% Expand - it's just here that the block ordering matters
% i.e. for the EMrow which we won't need anymore. The EM are always block
% sorted to begin with, since the GLMALPHA matrix is filled order by order.
G=Glma'*XYlmr;
[GK,VK]=deal([]);
if nargout>=4
if strcmp(lower(srt),'global') || strcmp(lower(srt),'belt')
GK=G(1:N,:);
VK=V(1:N);
end
end
% And now do the final truncation that you wanted in the first place
G=G(1:J,:);
V=V(1:J);
EM=EM(1:J);
% What do we predict the sum of the eigenfunctions squared is?
% See RB VI p89
% And check that it's all nicely done
if resc==0
if upco==0 && J==ldim
% This doesn't work if it's no longer full-dimensionsal
difer(abs(diag(G'*G)-NA),[],[],NaN);
elseif upco>0
a=upco;
p=(-((2*a+a^2+4*(L+1)*a+2*(L+1)*a^2+2)/...
((1+a)^2)^(L+1))+(2*a+a^2+2))/(a^2*(2+a)^2)/4/pi;
if J==ldim
difer(abs(diag(G'*G)-p),10,0);
end
elseif upco<0
a=abs(upco);
p=(((-2*a-a^2+4*(L+1)*a+2*(L+1)*a^2-2)*(1+a)^(2*L+4))-...
(-2*a-a^2-2)*(1+a)^2)/(a^2*(2+a)^2)/4/pi;
% Watch out, these numbers grow very large and accumulate errors!
% But the formula is, however, correct
if J==ldim
difer(abs(diag(G'*G)-p),10,0);
end
end
end
% Output
varns={G,V,EM,GK,VK,NA,N,theta,phi,Glma,EL};
varargout=varns(1:nargout);
elseif strcmp(TH,'demo1')
disp('Checking that internal block sorting has no effect whatsoever')
for srt={'global' 'local' 'belt'}
disp(srt)
[G0,V0,EM0,GK0,VK0,NA0,N0,theta0]=galpha([],[],1,[],[],srt,[],[],0);
[G1,V1,EM1,GK1,VK1,NA1,N1,theta1]=galpha([],[],1,[],[],srt,[],[],1);
difer([G0(:);V0(:);EM0(:);GK0(:);VK0(:);NA0(:);N0(:);theta0(:)]-...
[G1(:);V1(:);EM1(:);GK1(:);VK1(:);NA1(:);N1(:);theta1(:)])
TH=44; L=23;
[G,V,EM,GK,VK,NA,N,th]=galpha(TH,L,1,[],[],srt);
% This should be the Shannon number everywhere
difer(diag(G'*G)-NA)
% This should be close to the Shannon number in the region
plot(th*180/pi,diag(G'*diag(V)*G))
hold on
plot([TH TH],[0 NA],'k--')
axis tight
disp('Hit return to go on...')
pause
end
hold off
elseif strcmp(TH,'demo2')
sord=3;
TH=40; L=18; [G,V,EM,GK,VK,NA,N,th]=galpha(TH,L,sord,[],[],'global');
% To give a nice visual
clf
for index=1:(L+1)^2;
plot(th,G(index,:),'linew',2);
axis tight ; grid on ;
title(sprintf('%s= %i ; order m= %i ; %s = %8.6f',...
'\alpha',index,EM(index),'\lambda',V(index)));
ylim([-3.3 3.3]) ; disp('Hit return to go on...') ; pause
end
elseif strcmp(TH,'demo3')
sord=3;
TH=40; L=12; [G,V,EM,GK,VK,NA,N,~,~,Glma]=...
galpha(TH,L,sord,linspace(0,pi,50),linspace(0,2*pi,100),'global');
% Approximate normalization
[lon,lat]=Fibonacci_grid;
Gf=galpha(TH,L,sord,pi/2-lat*pi/180,lon*pi/180,'global',[],[],[],[],1);
% To give a nice visual
clf
for index=1:(L+1)^2;
imagesc(reshape(G(index,:),50,100));
% Check the normalization
norma=sum(Gf(index,:).*Gf(index,:))*[4*pi/length(Gf(index,:))];
disp(' '); disp(sprintf('Normalization over the sphere is %f',norma)); disp(' ')
% Check again using GLM2LMCOSI which now contains the renormalization
Gff=plm2xyz(glm2lmcosi(Glma,index),lat,lon);
norma=sum(sum(Gff.*Gff))*[4*pi/length(Gff)];
disp(' '); disp(sprintf('Normalization over the sphere is %f',norma)); disp(' ')
axis tight ; grid on ;
title(sprintf('%s= %i ; order m= %i ; %s = %8.6f',...
'\alpha',index,EM(index),'\lambda',V(index)));
disp('Hit return to go on...') ; pause
end
elseif strcmp(TH,'demo4')
sord=3;
TH=40; L=12; [G,V,EM,GK,VK,NA,N,th]=...
galpha(TH,L,sord,linspace(0,pi,50),linspace(0,2*pi,100),'global');
% Apply rotation
R=rots(L,V,EM,pi/6);
G=R*G;
clf
% To give a nice visual
for index=1:(L+1)^2;
imagef([],[],reshape(G(index,:),50,100));
axis tight ; grid on ;
title(sprintf('%s= %i ; order m= %i ; %s = %8.6f',...
'\alpha',index,EM(index),'\lambda',V(index)));
disp('Hit return to go on...') ; pause ; end
else
error('Specify valid demo string, see HELP GALPHA')
end