-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathNJCST.m
257 lines (228 loc) · 9.35 KB
/
NJCST.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
function [sum,i,termstor,eulterm] = NJCST(htype, kernelfcn, r, tol, err, maxite, varargin)
% [sum,i,termstor,eulterm] = NJCST(htype, kernelfn, r, tol, err, maxite, varargin)
%
% NJCST.m is part of the CR1Dmod forward modeling package. It calculates hankel
% or harmonic transform of the the type:
%
% G(r) = integral of [ FUNC(x) * JCS(x*r) * dx ] from 0 to infinity
%
% where JCS is one of the bessel or harmonic functions J0, J1, COS or SIN.
% The kernel function of the transformation (FUNC) is passed through the
% function handle 'kernelfcn'. Additional parameters passed to NJCST in the
% varargin cell array, will be passed on to FUNC after the integration
% parameter x.
%
% Input
% htype: 'J0', 'J1', 'COS' or 'SIN'
% kernelfcn: pointer to or expression for kernel function
% r: necessary constant of the transformation
% maxite: maximum number of segments of the besselfunction to integrate
% over (default and absolute max is 100)
% err: stop integration when last integration result was less than
% err.*sum_so_far (for both real and imag part) (default is 1e-5)
% tol: tolerance for the quadrature routine (default is 1e-6)
%
% Output
% sum: value of the transformation
% i: number of segments integrated
% termstor: array of calculated integration terms
% eulterm: array of euler-transformed terms. The result of summing all terms
% in this array, is the output parameter sum
%
% The function uses a quadrature rule to do the integration between pre-calculated
% zeros of the transform function. For the J0 and J1 functions the first 301
% zeros have been established. If more terms are, these are found by adding
% multiples of pi to the 301st zero. The terms calculated in this manner are
% stored in the termstor array, and an euler transformation is applied. New
% terms are added until the last (euler) term added to the sum is less than
% the sum multiplied by the parameter 'err', or until 'maxite' terms have been
% added.
% The euler transformation is not applied to the first 'startEuler-1' terms,
% and these terms will be calculated and summed regardless of the error
% criteria.
%
% The Euler transformation code used is modified from:
% Press, W.H., Flannery, B.P., Teukolsky, S.A., and Vetterling, W.T. (1986):
% Numerical Recipes, The Art of Numerical Computing. Cambridge University
% Press.
%
% The calling function can save the last two output terms, termstor and eulterm,
% and use them for calculating related transforms. I.e. it is often necessary
% to calculate another transform where the only difference in the kernel
% function is a power of the integration variable, x. This can be easily
% done by multiplying all the terms in termstor by x and then perform a new
% euler transformation on the terms. However no automatic procedure has been
% implemented to facilitat this, so far.
%
% Written by:
% Thomas Ingeman-Nielsen
% The Arctic Technology Center, BYG
% Technical University of Denmark
% Email: [email protected]
persistent S % S will hold precalculated values of the zeros of the transform functions
startEuler = 3;
% load precalculated zero-values and add more iff necessary.
switch htype
case {'J0'}
if ~isfield(S,'z_J') || isempty(S.z_J)
S = load('Zeros_J.mat','z_J');
end
if length(S.z_J)-1<maxite
S.z_J = [S.z_J, S.z_J(end)+(1:maxite-length(S.z_J)+1).*pi];
end
z = S.z_J;
quadfun = @J0;
case {'J1'}
if ~isfield(S,'z_J') || isempty(S.z_J)
S = load('zeros_J.mat','z_J');
end
if length(S.z_J)-1<maxite
S.z_J = [S.z_J, S.z_J(end)+(1:maxite-length(S.z_J)+1).*pi];
end
z = S.z_J;
quadfun = @J1;
case {'COS'}
if ~isfield(S,'z_C') || isempty(S.z_C)
S.z_C = [0, (1:2:maxite*2+2).*pi./2];
end
if length(S.z_C)-1<maxite
S.z_C = [S.z_C, S.z_C(end)+(1:maxite-length(S.z_C)+1).*pi./2];
end
z = S.z_C;
quadfun = @COSqfun;
case {'SIN'}
if ~isfield(S,'z_S') || isempty(S.z_S)
S.z_S = [0, (2:2:maxite*2+2).*pi./2];
end
if length(S.z_S)-1<maxite
S.z_S = [S.z_S, S.z_S(end)+(1:maxite-length(S.z_S)+1).*pi./2];
end
z = S.z_S;
quadfun = @SINqfun;
end
if isempty(err)
err = 1e-5;
end
termstor = [];
eulterm = [];
sum = 0;
i = 1; % i-1 is the number of segments calculated
lastwarn('');
warnState = warning('off', 'MATLAB:divideByZero');
% Since we integrate from 0, kernel functions which contains the term 1/x
% will give a divide by zero warning at the lower limit of integration. This
% is dealt with in quad by adding eps (the smallest number Matlab can
% handle) to the lower limit of integration.
term = quad(quadfun, z(i)/r, z(i+1)/r, tol, 0, kernelfcn, r, varargin{:});
if ~isfinite(term)
% if quad fails try quadl instead
term = quadl(quadfun, z(i)/r, z(i+1)/r, tol, 0, kernelfcn, r, varargin{:});
if ~isfinite(term)
disp(['NJCST error: neither quad or quadl was able to integrate '...
'the specified function']);
disp('Aborting calculation!');
termstor = [termstor; term];
return
end
end
warning(warnState);
termstor = [termstor; term];
sum = sum + term;
i = i+1;
% Calculate a few terms without euler transformation
% and without taking the error criteria into account.
while i-1<startEuler
drawnow; % This command empties the queue
term = quad(quadfun, z(i)/r, z(i+1)/r, tol, 0, kernelfcn, r, varargin{:});
if ~isfinite(term)
% if quad fails try quadl instead
term = quadl(quadfun, z(i)/r, z(i+1)/r, tol, 0, kernelfcn, r, varargin{:});
if ~isfinite(term)
disp(['NJCST error: neither quad or quadl was able to integrate '...
'the specified function']);
disp('Aborting calculation!');
termstor = [termstor; term];
return
end
end
termstor = [termstor; term];
sum = sum + term;
i = i+1;
end
if (abs(real(term))>=err*abs(real(sum))) || ((abs(imag(sum)) ~= 0) && (abs(imag(term))>=err*abs(imag(sum))))
term = quadl(quadfun, z(i)/r, z(i+1)/r, tol, 0, kernelfcn, r, varargin{:});
if ~isfinite(term)
% if quad fails try quadl instead
term = quadl(quadfun, z(i)/r, z(i+1)/r, tol, 0, kernelfcn, r, varargin{:});
if ~isfinite(term)
disp(['NJCST error: neither quad or quadl was able to integrate '...
'the specified function']);
disp('Aborting calculation!');
termstor = [termstor; term];
return
end
end
termstor = [termstor; term];
i = i+1;
% The following Euler convergence improvement code is
% modified from 'Numerical Recepies in C++'
wkspc(1) = term;
nterm = 1;
sum = sum + 0.5.*term;
newterm = sum;
eulterm = zeros(size(termstor));
% Here we begin the main loop
while i-1<=maxite && ((abs(real(newterm))>=err*abs(real(sum)))...
|| ((abs(imag(sum)) ~= 0) && (abs(imag(newterm))>=err*abs(imag(sum)))))
drawnow; % This command empties the queue
term = quadl(quadfun, z(i)/r, z(i+1)/r, tol, 0, kernelfcn, r, varargin{:});
if ~isfinite(term)
% if quad fails try quadl instead
term = quadl(quadfun, z(i)/r, z(i+1)/r, tol, 0, kernelfcn, r, varargin{:});
if ~isfinite(term)
disp(['NJCST error: neither quad or quadl was able to integrate '...
'the specified function']);
disp('Aborting calculation!');
termstor = [termstor; term];
return
end
end
tmp = wkspc(1);
wkspc(1) = term;
for k=1:nterm-1
dum = wkspc(k+1);
wkspc(k+1) = 0.5.*(wkspc(k)+tmp);
tmp = dum;
end
wkspc(nterm+1) = 0.5.*(wkspc(nterm)+tmp);
if (abs(wkspc(nterm+1))) <= (abs(wkspc(nterm)))
nterm = nterm+1;
newterm = (0.5.*wkspc(nterm));
sum = sum + newterm;
else
newterm = wkspc(nterm+1);
sum = sum + newterm;
end
eulterm = [eulterm;newterm];
i = i+1;
end
end
i = i-1; % This the number of segments included
if isappdata(0,'debug') && getappdata(0,'debug')
disp(['R: ' sprintf('%6.3f',r) ' N-terms: ' sprintf('%3.0i',i) ' Hank: ' sprintf('%e +i* %e',real(sum), imag(sum))]);
end
% *********************************************************************
% * Kernel functions
% *********************************************************************
function Q = J0(lambda, kernelfcn, r, varargin)
tmp = besselj(0,lambda.*r);
Q = feval(kernelfcn, lambda, varargin{:}).*tmp;
function Q = J1(lambda, kernelfcn, r, varargin)
tmp = besselj(1,lambda.*r);
Q = feval(kernelfcn, lambda, varargin{:}).*tmp;
function Q = COSqfun(lambda, kernelfcn, r, varargin)
tmp = cos(lambda.*r);
Q = feval(kernelfcn, lambda, varargin{:}).*tmp;
function Q = SINqfun(lambda, kernelfcn, r, varargin)
tmp = sin(lambda.*r);
Q = feval(kernelfcn, lambda, varargin{:}).*tmp;