forked from ThorstenHellert/SC
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSCdynamicAperture.m
More file actions
254 lines (222 loc) · 6.53 KB
/
SCdynamicAperture.m
File metadata and controls
254 lines (222 loc) · 6.53 KB
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
function [DA,RMAXs,thetas] = SCdynamicAperture(RING,dE,varargin)
% SCdynamicAperture
% =================
%
% NAME
% ----
% SCdynamicAperture - calculates the dynamic aperture of a ring
%
% SYNOPSIS
% --------
% `[DAs, RMAXs, thetas] = SCdynamicAperture(RING, dE [, options])`
%
%
% DESCRIPTION
% -----------
% Calculates the dynamic aperture (i.e. the area of stable particle motion) of
% `RING` at energy `dE`. The general strategy is to find the
% maximum radii at which the particle motion is stable along a number of
% straight lines, leaving the origin at angles `thetas`. The dynamic aperture
% is then approximated as the area of the resulting polygon.
%
%
% INPUT
% -----
% `RING`::
% Lattice cell structure.
% `dE`::
% Momentum deviation.
%
% OPTIONS
% -------
% The following options can be specified as name-value pairs:
%
% `'bounds'` (`[0,1e-3]`)::
% a 1x2 array containing a best guess for the upper and lower boundaries
% of the maximum radius. These inital boundaries are automatically
% refined in this routine, so a rough guess is good enough.
% `'nturns'` (1000)::
% number of turns used to determine whether a particle is stable.
% `'thetas'` (`linspace(0,2*pi,16)`)::
% angles at which the maximum radii are evaluated.
% `'accuracy'` (`1e-6`)::
% is the accuracy to which the dynamic aperture radii are determined [m].
% `'launchOnOrbit'` (0)::
% If true, particles are launched on closed orbit, otherwise on axis
% `'centerOnOrbit'` (1)::
% If true, the closed orbit is subtracted from the DA coordinates, which
% is advised for a corrected machine with support structure misalignments.
% `'useOrbit6'` (0)::
% If true, findorbit6 is used to determine the closed orbit, otherwise findorbit4
% `'auto'` (0)::
% if >0, this number of automatically determined sampling points is used,
% taking into account a presumed near-elliptical shape of the DA. In this
% case `'thetas'` is ignored.
% `'plot'` (0)::
% if true, progress is plotted.
% `'verbose'` (0)::
% if true, debug messages are printed.
%
% GLOBALS
% -------
% `runParallel`:: If true, a parfor loop is executed instead of a regular for loop.
%
% RETURN VALUES
% -------------
% `DA`::
% Dynamic aperture in m^2.
% `RMAXs`::
% Maximum radii at the evaluated `thetas`. `length(thetas)` array.
% `thetas`::
% Angles at which the maximum radii were evaluated.
% Parse input
p = inputParser;
addOptional(p,'bounds',[0,1e-3]);
addOptional(p,'nturns',1000);
addOptional(p,'nsteps',0);
addOptional(p,'thetas',linspace(0,2*pi,16));
addOptional(p,'accuracy',1e-6);
addOptional(p,'launchOnOrbit',0);
addOptional(p,'centerOnOrbit',1);
addOptional(p,'useOrbit6',0);
addOptional(p,'auto',0);
addOptional(p,'plot',0);
addOptional(p,'verbose',0);
parse(p,varargin{:});
par = p.Results;
inibounds = par.bounds;
nturns = par.nturns;
thetas = par.thetas;
% Check if parallel computation should be used
global runParallel
if runParallel
parforArg = Inf;
else
parforArg = 0;
end
% Issue warning if nsteps was defined
if par.nsteps~=0; warning('nsteps no longer supported; continuing with binary search.'); end
% If requested, auto generate thetas
if par.auto>0; [~,thetas] = autothetas(RING,dE,par.auto,varargin{:}); end
[~,sidx] = sort(abs(inibounds)); % Sort bounds w.r.t absolute value
inibounds = inibounds(sidx);
ZCO = zeros(6,1);
if par.launchOnOrbit
if par.useOrbit6
ZCO = findorbit6(RING);
else
tmp = findorbit4(RING,0);
if ~isnan(tmp(1))
ZCO(1:4) = tmp;
end
end
end
% Add energy offset
ZCO(5) = ZCO(5) + dE;
RMAXs = nan(length(thetas),1); % Initialize output array
DA = nan;
parfor (cntt = 1:length(thetas),parforArg) % Loop over angles
theta=thetas(cntt);
limits = inibounds;
fatpass(RING,nan(6,1),1,1,[1]); % Fake Track to initialize lattice
% Scale boundaries up until maxr is included
scales=0;
while scales<16
if check_bounds(RING,ZCO,nturns,theta,limits); break; end
limits = scale_bounds(limits,10);
scales = scales + 1;
if par.verbose; fprintf('Scaled: %e %e\n',limits(1),limits(2)); end
end
% Refine boundaries until requested accuracy is reached
while abs(limits(2)-limits(1)) > par.accuracy
limits = refine_bounds(RING,ZCO,nturns,theta,limits);
if par.verbose; fprintf('Refined: %e %e\n',limits(1),limits(2)); end
end
RMAXs(cntt)=mean(limits); % Store mean of final boundaries
end
if par.plot
figure(6232);
scatter(cos(thetas)'.*RMAXs,sin(thetas)'.*RMAXs);
% set(gca,'xlim',18E-3*[-1 1],'ylim',18E-3*[-1 1])
drawnow;
end
% Calculate DA area
dthetas = diff(thetas);
r0 = RMAXs(1:(end-1));
r1 = RMAXs(2:end);
DA = sum(sin(dthetas) .* r0' .* r1' / 2.);
% Center DA around closed orbit
if par.centerOnOrbit
if par.useOrbit6
tmp = findorbit6(RING);
else
tmp = findorbit4(RING,0);
end
if ~isnan(tmp(1))
[x,y] = pol2cart(thetas,RMAXs');
x = x - tmp(1);
y = y - tmp(3);
[thetas,RMAXs] = cart2pol(x,y);
RMAXs = RMAXs';
end
end
end
function res = check_bounds(RING,ZCO,nturns,theta,boundsIn)
rmin = boundsIn(1);
rmax = boundsIn(2);
Zmin = ZCO;
Zmin(1) = rmin * cos(theta);
Zmin(3) = rmin * sin(theta);
Zmax = ZCO;
Zmax(1) = rmax * cos(theta);
Zmax(3) = rmax * sin(theta);
ROUT = fatpass(RING,[Zmin,Zmax],0,nturns,[1]); % Track
RLAST = ROUT(:,end-1:end); % Get positions after last turn
if (~isnan(RLAST(1,1)) && isnan(RLAST(1,2)))
res = true;
else
res = false;
end
end
function bounds = refine_bounds(RING,ZCO,nturns,theta,boundsIn)
rmin = boundsIn(1);
rmax = boundsIn(2);
rmid = mean(boundsIn);
Z = ZCO;
Z(1) = rmid * cos(theta);
Z(3) = rmid * sin(theta);
ROUT = fatpass(RING,Z,0,nturns,[1]); % Track
RLAST = ROUT(:,end); % Get positions after last turn
if isnan(RLAST(1)) % Midpoint is outside DA
bounds = [rmin,rmid];
else % Midpoint is within DA
bounds = [rmid,rmax];
end
end
function out = scale_bounds(limits,alpha)
lower = mean(limits)-(mean(limits)-limits(1)) * alpha;
upper = mean(limits)-(mean(limits)-limits(2)) * alpha;
% Prohibit zero-crossing during scaling
if sign(lower) ~= sign(limits(1))
lower = 0.0;
end
if sign(upper) ~= sign(limits(2))
upper = 0.0;
end
out = [lower,upper];
end
function r = fatpass(varargin)
% fatpass - fake atpass to circumvent some madlab parfor shizzle. I don't care anymore.
r = atpass(varargin{:});
end
function [rout,tout] = autothetas(RING,dE,nt,varargin)
tin = linspace(0,2*pi*3/4,4);
[DA,rs,ts] = SCdynamicAperture(RING,dE,varargin{:},'thetas',tin,'auto',0);
a=(rs(1)+rs(3))/2;
b=(rs(2)+rs(4))/2;
e=sqrt(1-b^2/a^2);
mu=acosh(1/e);
nu=linspace(0,2*pi,nt);
rout = abs(cosh(mu+1i*nu));
tout = angle(cosh(mu+1i*nu));
end