1 | function [OCS, SmatNoWeights, S, U, V] = orbitcorrectionmethods(OCS, Smat, S, U, V) |
---|
2 | %ORBITCORRECTIONMETHODS - Some the orbit correction methods used on light sources |
---|
3 | % |
---|
4 | % [OCS, Smat, S, U, V] = orbitcorrectionmethods(OCS) % Get response matrix & compute SVD correction |
---|
5 | % [OCS, Smat, S, U, V] = orbitcorrectionmethods(OCS, Smat) % Compute SVD & correction |
---|
6 | % [OCS, Smat, S, U, V] = orbitcorrectionmethods(OCS, Smat, S, U, V) % Compute correction |
---|
7 | % |
---|
8 | % OCS.FitRF - Determines which RF freqency method to use |
---|
9 | % See setorbit for information on all the different flags. |
---|
10 | % |
---|
11 | % NOTES |
---|
12 | % 1. OCS.CM.Data is not changed by this function |
---|
13 | % OCS.CM.Delta is the delta correction |
---|
14 | % 2. Both row and column weighting of the response matrix can be done. |
---|
15 | % The default is unity row weights and column weight by the std of each column. |
---|
16 | % |
---|
17 | % See also setorbit, setorbitbump, rmdisp, plotcm |
---|
18 | |
---|
19 | % |
---|
20 | % Written by Gregory J. Portmann |
---|
21 | % Addition for Soleil by Laurent S. Nadolski |
---|
22 | % Constraints on min and max RF frequency variations |
---|
23 | |
---|
24 | % T = V(:,OCS.SVDIndex) * diag(S(OCS.SVDIndex).^(-1)) * U(:,OCS.SVDIndex)'; |
---|
25 | % Delcm = T * (BPMWeight .* (GoalOrbitVec - StartOrbitVec)); |
---|
26 | % |
---|
27 | |
---|
28 | |
---|
29 | % Initialize |
---|
30 | FitRFDefault = 0; |
---|
31 | |
---|
32 | % Machine dependent |
---|
33 | if strcmpi(getfamilydata('SubMachine'),'StorageRing') && strcmpi(getfamilydata('Machine'),'SOLEIL') |
---|
34 | DeltaRFmin = 1e-7; % MHz minimum variation allowed by Master oscillator |
---|
35 | DeltaRFmax = 100e-6; % MHz maximum variation allowed in one step |
---|
36 | else |
---|
37 | DeltaRFmin = 1e-17; % MHz minimum variation allowed by Master oscillator |
---|
38 | DeltaRFmax = 100e6; % MHz maximum variation allowed in one step |
---|
39 | end |
---|
40 | |
---|
41 | |
---|
42 | % Input checking (should add more) |
---|
43 | if nargin < 2 |
---|
44 | Smat = []; |
---|
45 | end |
---|
46 | if nargin < 3 |
---|
47 | S = []; |
---|
48 | U = []; |
---|
49 | V = []; |
---|
50 | end |
---|
51 | |
---|
52 | |
---|
53 | % This function expects .BPM and .CM to be cell arrays. |
---|
54 | % This gets put back at the end of the function. |
---|
55 | if ~iscell(OCS.BPM) |
---|
56 | NoCellBPMFlag = 1; |
---|
57 | OCS.BPM = {OCS.BPM}; |
---|
58 | OCS.GoalOrbit = {OCS.GoalOrbit}; |
---|
59 | if isfield(OCS, 'BPMWeight') |
---|
60 | OCS.BPMWeight = {OCS.BPMWeight}; |
---|
61 | end |
---|
62 | else |
---|
63 | NoCellBPMFlag = 0; |
---|
64 | end |
---|
65 | if ~iscell(OCS.CM) |
---|
66 | NoCellCMFlag = 1; |
---|
67 | OCS.CM = {OCS.CM}; |
---|
68 | if isfield(OCS, 'CMWeight') |
---|
69 | if ~iscell(OCS.CMWeight) |
---|
70 | OCS.CMWeight = {OCS.CMWeight}; |
---|
71 | end |
---|
72 | end |
---|
73 | else |
---|
74 | NoCellCMFlag = 0; |
---|
75 | end |
---|
76 | |
---|
77 | |
---|
78 | % RF frequency methods in orbit correction |
---|
79 | if ~isfield(OCS, 'FitRF') |
---|
80 | OCS.FitRF = FitRFDefault; |
---|
81 | end |
---|
82 | if isempty(OCS.FitRF) |
---|
83 | OCS.FitRF = FitRFDefault; |
---|
84 | end |
---|
85 | |
---|
86 | |
---|
87 | %%%%%%%%%%%%%%%% |
---|
88 | % The SVD Part % |
---|
89 | %%%%%%%%%%%%%%%% |
---|
90 | if isempty(U) |
---|
91 | %%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
92 | % Get the response matrix % |
---|
93 | %%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
94 | if isempty(Smat) |
---|
95 | for i = 1:length(OCS.BPM) |
---|
96 | Srow = []; |
---|
97 | for j = 1:length(OCS.CM) |
---|
98 | %if ModelRespFlag == 1 |
---|
99 | if any(strcmpi(OCS.Flags,'ModelResp')) |
---|
100 | % When using measbpmresp('Model') |
---|
101 | % Rmat(1,1) is always horizontal |
---|
102 | % Rmat(2,2) is always vertical |
---|
103 | Rmat = measbpmresp('model',OCS.BPM{i},OCS.BPM{i}, OCS.CM{j},OCS.CM{j}, OCS.BPM{i}.Units); |
---|
104 | if ismemberof(OCS.BPM{i}.FamilyName, gethbpmfamily) && ismemberof(OCS.CM{j}.FamilyName,gethcmfamily) |
---|
105 | S = Rmat(1,1).Data; |
---|
106 | elseif ismemberof(OCS.BPM{i}.FamilyName, getvbpmfamily) && ismemberof(OCS.CM{j}.FamilyName,gethcmfamily) |
---|
107 | S = Rmat(2,1).Data; |
---|
108 | elseif ismemberof(OCS.BPM{i}.FamilyName, gethbpmfamily) && ismemberof(OCS.CM{j}.FamilyName,getvcmfamily) |
---|
109 | S = Rmat(1,2).Data; |
---|
110 | elseif ismemberof(OCS.BPM{i}.FamilyName, getvbpmfamily) && ismemberof(OCS.CM{j}.FamilyName,getvcmfamily) |
---|
111 | S = Rmat(2,2).Data; |
---|
112 | else |
---|
113 | error('Problem getting the model response matrix'); |
---|
114 | end |
---|
115 | else |
---|
116 | % Get the golden BPM response matrix |
---|
117 | [S, FileName] = getrespmat(OCS.BPM{i}, OCS.CM{j}, 'Numeric'); |
---|
118 | if any(find(isnan(S))) |
---|
119 | error('Not all BPMs and correctors exist in the response matrix.'); |
---|
120 | end |
---|
121 | end |
---|
122 | if length(OCS.GoalOrbit{i}) ~= size(S,1) |
---|
123 | error('The goal orbit vector length does not equal the response matrix row length'); |
---|
124 | end |
---|
125 | |
---|
126 | Srow = [Srow S]; |
---|
127 | end |
---|
128 | Smat = [Smat; Srow]; |
---|
129 | end |
---|
130 | %SmatCheck = measbpmresp('Model','Numeric'); |
---|
131 | |
---|
132 | |
---|
133 | %%%%%%%%%%%%%%%%%% |
---|
134 | % Get Dispersion % |
---|
135 | %%%%%%%%%%%%%%%%%% |
---|
136 | Eta = []; |
---|
137 | if OCS.FitRF |
---|
138 | for i = 1:length(OCS.BPM) |
---|
139 | if any(strcmpi(OCS.Flags,'ModelDisp')) |
---|
140 | %if strcmpi(DispFlag,'ModelDisp') |
---|
141 | DispOrbit = measdisp(OCS.BPM{i}, 'Hardware', 'Numeric', 'Model'); |
---|
142 | elseif any(strcmpi(OCS.Flags,'MeasDisp')) |
---|
143 | %elseif strcmpi(DispFlag,'MeasDisp') |
---|
144 | DispOrbit = measdisp(OCS.BPM{i}, 'Hardware', 'Numeric'); |
---|
145 | elseif any(strcmpi(OCS.Flags,'GoldenDisp')) |
---|
146 | %elseif strcmpi(DispFlag,'GoldenDisp') |
---|
147 | DispOrbit = getdisp(OCS.BPM{i}, 'Hardware', 'Numeric'); |
---|
148 | end |
---|
149 | |
---|
150 | if strcmpi(OCS.BPM{i}.Units, 'Physics') |
---|
151 | % Put the RF change in physics units, not energy change |
---|
152 | RFhw = physics2hw(OCS.RF); |
---|
153 | DispOrbit = DispOrbit / (OCS.RF.Data / RFhw.Data); |
---|
154 | end |
---|
155 | |
---|
156 | if isempty(DispOrbit) |
---|
157 | error('Did not find or generate dispersion orbit properly'); |
---|
158 | end |
---|
159 | if any(isnan(DispOrbit)) |
---|
160 | error('The dispersion data has at least one NaN.'); |
---|
161 | end |
---|
162 | |
---|
163 | Eta = [Eta; DispOrbit]; |
---|
164 | end |
---|
165 | end |
---|
166 | OCS.Eta = Eta; |
---|
167 | end |
---|
168 | end |
---|
169 | |
---|
170 | |
---|
171 | % Save a weight free response matrix |
---|
172 | SmatNoWeights = Smat; |
---|
173 | |
---|
174 | |
---|
175 | %%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
176 | % Build the orbit vectors % |
---|
177 | %%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
178 | StartOrbitVec = []; |
---|
179 | GoalOrbitVec = []; |
---|
180 | BPMWeight = []; |
---|
181 | for i = 1:length(OCS.BPM) |
---|
182 | StartOrbitVec = [StartOrbitVec; OCS.BPM{i}.Data(:)]; |
---|
183 | GoalOrbitVec = [GoalOrbitVec; OCS.GoalOrbit{i}(:)]; |
---|
184 | |
---|
185 | if ~isfield(OCS, 'BPMWeight') || isempty(OCS.BPMWeight{i}) |
---|
186 | BPMWeight = [BPMWeight; ones(length(OCS.BPM{i}.Data),1)]; |
---|
187 | elseif isscalar(OCS.BPMWeight{i}) |
---|
188 | BPMWeight = [BPMWeight; ones(length(OCS.BPM{i}.Data),1) * OCS.BPMWeight{i}]; |
---|
189 | else |
---|
190 | BPMWeight = [BPMWeight; OCS.BPMWeight{i}(:)]; |
---|
191 | end |
---|
192 | end |
---|
193 | |
---|
194 | |
---|
195 | % Build column weight vector |
---|
196 | CMWeight = []; |
---|
197 | for i = 1:length(OCS.CM) |
---|
198 | if ~isfield(OCS, 'CMWeight') || isempty(OCS.CMWeight{i}) |
---|
199 | % When using all singular values this does not do anything |
---|
200 | % The amp to radian conversion is probably a better normalizer |
---|
201 | %CMWeight = 1 ./ sqrt(sum(Smat.^2)); |
---|
202 | CMWeight = 1./std(Smat)'; |
---|
203 | |
---|
204 | % No weight |
---|
205 | %CMWeight = ones(size(Smat,2),1); |
---|
206 | |
---|
207 | break; |
---|
208 | %CMWeight = [CMWeight; ones(length(OCS.CM{i}.Data),1)]; |
---|
209 | |
---|
210 | elseif isscalar(OCS.CMWeight{i}) |
---|
211 | CMWeight = [CMWeight; ones(length(OCS.CM{i}.Data),1) * OCS.CMWeight{i}]; |
---|
212 | |
---|
213 | else |
---|
214 | CMWeight = [CMWeight; OCS.CMWeight{i}(:)]; |
---|
215 | end |
---|
216 | end |
---|
217 | |
---|
218 | |
---|
219 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
220 | % Add a column weight to the response matrix % |
---|
221 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
222 | %if isempty(U) |
---|
223 | for j = 1:length(CMWeight) |
---|
224 | Smat(:,j) = Smat(:,j) * CMWeight(j); |
---|
225 | end |
---|
226 | %end |
---|
227 | |
---|
228 | |
---|
229 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
230 | % Add a weighted disperion as a column of the response matrix % |
---|
231 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
232 | if OCS.FitRF == 0 |
---|
233 | % Don't fit the RF frequency |
---|
234 | OCS.DeltaRF = 0; |
---|
235 | elseif any(OCS.FitRF == [1 2 3 4 5]) |
---|
236 | % Column weighting (changing the units or sensitivity) seems to be a good thing. |
---|
237 | % (At least at the ALS working in hardware units.) |
---|
238 | % Weight by more than the average std of Smat also seems to give good results but |
---|
239 | % I'm not sure it's necessary or should be done. |
---|
240 | RFWeight = 10 * mean(std(Smat)) / std(OCS.Eta); |
---|
241 | Smat = [Smat RFWeight*OCS.Eta]; |
---|
242 | else |
---|
243 | error('Unknown RF correction method.'); |
---|
244 | end |
---|
245 | |
---|
246 | |
---|
247 | |
---|
248 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
249 | % Add a row weight to the response matrix % |
---|
250 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
251 | % Weighted LS |
---|
252 | % Weighted LS was developed for heteroscedastic errors but it's used here for other reasons |
---|
253 | % W*(Mmeas - Mmodel) = W*A*b + W*e, where the e's are gausian, zero mean, independent, but not constant variance |
---|
254 | % W is chosen to make E(Wee'W') = sigma * I (ie, W * e has a constant variance) |
---|
255 | % |
---|
256 | % The new LS equations are, |
---|
257 | % b = inv(Amod'*W' * W*Amod) * Amod'*W' * W*(Xgoal - Xmeasured); % Parameter fit |
---|
258 | % b = V(:,Ivec) * b; |
---|
259 | % bvar = inv(Amod'*W'*W*Amod); |
---|
260 | % |
---|
261 | % Since the weight matrix, W, is only going to be diagonal, it is less memory |
---|
262 | % to scaled Amod and (Mmeas-Model) by the diagonal term and not create the matrix W |
---|
263 | if ~all(BPMWeight == 1) |
---|
264 | for i = 1:size(Smat,1) |
---|
265 | Smat(i,:) = BPMWeight(i) * Smat(i,:); |
---|
266 | end |
---|
267 | end |
---|
268 | |
---|
269 | |
---|
270 | %%%%%%%%%%%%%%%% |
---|
271 | % The SVD Part % |
---|
272 | %%%%%%%%%%%%%%%% |
---|
273 | if isempty(U) |
---|
274 | if any(any(isnan(Smat))) |
---|
275 | error('The response matrix has at least one NaN.'); |
---|
276 | end |
---|
277 | |
---|
278 | % Do the SVD |
---|
279 | [U, S, V] = svd(Smat, 0); %'econ'); |
---|
280 | S = diag(S); |
---|
281 | end |
---|
282 | |
---|
283 | |
---|
284 | % Determine the singular vector and error check |
---|
285 | if isnumeric(OCS.SVDIndex) |
---|
286 | if length(OCS.SVDIndex) == 1 |
---|
287 | if rem(OCS.SVDIndex,1) == 0 |
---|
288 | OCS.SVDIndex = 1:OCS.SVDIndex; |
---|
289 | else |
---|
290 | % Use threshold |
---|
291 | Ivec = find(S > max(S)*OCS.SVDIndex); |
---|
292 | OCS.SVDIndex = Ivec; |
---|
293 | end |
---|
294 | end |
---|
295 | elseif ischar(OCS.SVDIndex) |
---|
296 | if strcmpi(OCS.SVDIndex, 'All') |
---|
297 | OCS.SVDIndex = 1:length(S); |
---|
298 | else |
---|
299 | error('OCS.SVDIndex unknown'); |
---|
300 | end |
---|
301 | else |
---|
302 | error('OCS.SVDIndex unknown'); |
---|
303 | end |
---|
304 | |
---|
305 | % Nothing greater then the total number is singular values |
---|
306 | i = find(OCS.SVDIndex > length(S)); |
---|
307 | if ~isempty(i) |
---|
308 | warning('Requested number of singular values is greater than the total. Removing %d values.',length(i)); |
---|
309 | OCS.SVDIndex(i) = length(S); |
---|
310 | end |
---|
311 | |
---|
312 | % No non-integers |
---|
313 | OCS.SVDIndex = round(OCS.SVDIndex); |
---|
314 | |
---|
315 | % Nothing less than zero |
---|
316 | i = find(OCS.SVDIndex <= 0); |
---|
317 | OCS.SVDIndex(i) = length(S); |
---|
318 | |
---|
319 | % No repeats |
---|
320 | OCS.SVDIndex = sort(OCS.SVDIndex); |
---|
321 | i = find(diff(OCS.SVDIndex)==0); |
---|
322 | OCS.SVDIndex(i) = []; |
---|
323 | |
---|
324 | if isempty(OCS.SVDIndex) |
---|
325 | error('Singular values vector is empty'); |
---|
326 | end |
---|
327 | |
---|
328 | |
---|
329 | if OCS.FitRF == 2 || OCS.FitRF == 3 |
---|
330 | % Constrained L.S. with SVD |
---|
331 | % Constraint: Sum of the energy change to zero |
---|
332 | % Note: Model required |
---|
333 | |
---|
334 | R = []; |
---|
335 | HCMEnergyChangeTotal = 0; |
---|
336 | for i = 1:length(OCS.CM) |
---|
337 | if ismemberof(OCS.CM{i}.FamilyName, 'HCOR') |
---|
338 | L = getfamilydata('Circumference'); |
---|
339 | |
---|
340 | if ~isfield(OCS.CM{i}, 'Dispersion') |
---|
341 | % Dispersion at the correctors in physics units |
---|
342 | [OCS.CM{i}.Dispersion, DyHCM] = modeldisp([], OCS.CM{i}.FamilyName, OCS.CM{i}.DeviceList, 'Numeric', 'Physics'); |
---|
343 | end |
---|
344 | |
---|
345 | % Present corrector setpoint in physics units |
---|
346 | HCM = hw2physics(OCS.CM{i}); |
---|
347 | |
---|
348 | |
---|
349 | % Move ALS specific code to setorbitdefault ??? |
---|
350 | if strcmpi(getfamilydata('Machine'),'ALS') |
---|
351 | % For the ALS, either the HCMCHICANE families need to be included or |
---|
352 | % the chicane "part" of the HCMs needs to be removed. This will remove the chicane. |
---|
353 | Energy = getfamilydata('Energy'); |
---|
354 | |
---|
355 | % Sector 6 |
---|
356 | % Off 1.9 GeV 1.5 GeV |
---|
357 | % HCMCHICANEM(6,1) 80.0 18.0 ? |
---|
358 | % HCMCHICANEM(6,1) 80.0 20.0 ? |
---|
359 | % HCM(6,1) 0.0 18.8 ? |
---|
360 | ihcm = findrowindex([6 1], OCS.CM{i}.DeviceList); |
---|
361 | if length(ihcm) == 1 |
---|
362 | try |
---|
363 | if getsp('HCMCHICANEM',[6 1]) < 70 |
---|
364 | % Assume sector 6 chicane is on |
---|
365 | if Energy == 1.9 |
---|
366 | HCM.Data(ihcm) = HCM.Data(ihcm) + hw2physics(OCS.CM{i}.FamilyName, OCS.CM{i}.Field, -18.8, [6 1]); |
---|
367 | else |
---|
368 | HCM.Data(ihcm) = HCM.Data(ihcm) + hw2physics(OCS.CM{i}.FamilyName, OCS.CM{i}.Field, -18.8*1.5/1.9, [6 1]); |
---|
369 | end |
---|
370 | end |
---|
371 | catch |
---|
372 | fprintf('%s\n', lasterr); |
---|
373 | fprintf('Problem reading HCMCHICANEM(6,1). The chicane "offset" on HCM(6,1) will not be removed.\n\n'); |
---|
374 | end |
---|
375 | end |
---|
376 | |
---|
377 | % Sector 11 |
---|
378 | % Off 1.9 GeV 1.5 GeV |
---|
379 | % HCMCHICANEM(11,1) 80.0 40.5 52.0 |
---|
380 | % HCMCHICANEM(11,1) 80.0 40.5 52.0 |
---|
381 | % HCM(10,8) 0.0 -17.0 -14.0 |
---|
382 | % HCM(11,1) 0.0 -17.0 -14.0 |
---|
383 | ihcm = findrowindex([10 8], OCS.CM{i}.DeviceList); |
---|
384 | if length(ihcm) == 1 |
---|
385 | try |
---|
386 | if getsp('HCMCHICANEM',[11 1]) < 60 |
---|
387 | % Assume sector 11 chicane is on |
---|
388 | if Energy == 1.9 |
---|
389 | HCM.Data(ihcm) = HCM.Data(ihcm) + hw2physics(OCS.CM{i}.FamilyName, OCS.CM{i}.Field, [17; 17], [10 8;11 1]); |
---|
390 | else |
---|
391 | HCM.Data(ihcm) = HCM.Data(ihcm) + hw2physics(OCS.CM{i}.FamilyName, OCS.CM{i}.Field, [14; 14], [10 8;11 1]); |
---|
392 | end |
---|
393 | end |
---|
394 | catch |
---|
395 | fprintf('%s\n', lasterr); |
---|
396 | fprintf('Due to an error, the chicane "offset" on HCM(10,8) will not be removed.n\n'); |
---|
397 | end |
---|
398 | end |
---|
399 | ihcm = findrowindex([11 1], OCS.CM{i}.DeviceList); |
---|
400 | if length(ihcm) == 1 |
---|
401 | try |
---|
402 | if getsp('HCMCHICANEM',[11 1]) < 60 |
---|
403 | % Assume sector 11 chicane is on |
---|
404 | if Energy == 1.9 |
---|
405 | HCM.Data(ihcm) = HCM.Data(ihcm) + hw2physics(OCS.CM{i}.FamilyName, OCS.CM{i}.Field, [17; 17], [10 8;11 1]); |
---|
406 | else |
---|
407 | HCM.Data(ihcm) = HCM.Data(ihcm) + hw2physics(OCS.CM{i}.FamilyName, OCS.CM{i}.Field, [14; 14], [10 8;11 1]); |
---|
408 | end |
---|
409 | end |
---|
410 | catch |
---|
411 | fprintf('%s\n', lasterr); |
---|
412 | fprintf('Due to an error, the chicane "offset" on HCM(11,1) will not be removed.n\n'); |
---|
413 | end |
---|
414 | end |
---|
415 | end |
---|
416 | % END ALS only |
---|
417 | |
---|
418 | |
---|
419 | % Energy change to remove |
---|
420 | HCMEnergyChangeTotal = HCMEnergyChangeTotal + sum(-1 * HCM.Data .* OCS.CM{i}.Dispersion / getmcf / L); |
---|
421 | |
---|
422 | % Energy scaling must be in physics units, hence the (HCM.Data./OCS.CM{i}.Data) |
---|
423 | if strcmpi(OCS.CM{i}.Units, 'Hardware') |
---|
424 | % PhysicsUnitsScaling = (HCM.Data./OCS.CM{i}.Data); %This does not work for zero setpoints |
---|
425 | PhysicsUnitsScaling = hw2physics(OCS.CM{i}. FamilyName,OCS.CM{i}.Field, 1, OCS.CM{i}.DeviceList); |
---|
426 | else |
---|
427 | PhysicsUnitsScaling = 1; |
---|
428 | end |
---|
429 | HCMEnergyChangeScalar = -1 * PhysicsUnitsScaling .* OCS.CM{i}.Dispersion / getmcf / L; |
---|
430 | R = [R HCMEnergyChangeScalar(:)']; |
---|
431 | |
---|
432 | % Sum of correctors to zero |
---|
433 | %R = [R ones(size(HCMEnergyChange(:)'))]; |
---|
434 | else |
---|
435 | R = [R zeros(1,size(OCS.CM{i}.DeviceList,1))]; |
---|
436 | end |
---|
437 | end |
---|
438 | |
---|
439 | if OCS.FitRF == 2 |
---|
440 | % Also remove the energy change due to the present corrector setpoints |
---|
441 | r = -HCMEnergyChangeTotal/2; % Not sure about the divide by 2 |
---|
442 | elseif OCS.FitRF == 3 |
---|
443 | % Only remove energy change due to the incremental change in the correctors |
---|
444 | r = 0; |
---|
445 | end |
---|
446 | |
---|
447 | % Add a zero for no constaint on the RF |
---|
448 | R = [R 0]; |
---|
449 | |
---|
450 | |
---|
451 | % Projection onto the columns of Smat*V (or U) |
---|
452 | % Since the correctors are V*b, the restriction in corrector strength is R*V |
---|
453 | X = Smat * V(:,OCS.SVDIndex); |
---|
454 | R = R * V(:,OCS.SVDIndex); |
---|
455 | b = inv(X'*X)*X' * (BPMWeight .* (GoalOrbitVec - StartOrbitVec)); |
---|
456 | br = b - inv(X'*X)*R'*inv(R*inv(X'*X)*R')*(R*b-r); |
---|
457 | Delcm = V(:,OCS.SVDIndex) * br; |
---|
458 | |
---|
459 | elseif OCS.FitRF == 4 |
---|
460 | % Constrained L.S. with SVD |
---|
461 | % Constraint: dot(DispersionX, Smat * dHCM) = 0 |
---|
462 | % or dot(DispersionX' * Smat, dHCM) = 0 or total dot(DisperionX, Smat * HCM) |
---|
463 | R = []; |
---|
464 | DispDotSmatHCM = 0; |
---|
465 | for i = 1:length(OCS.BPM) |
---|
466 | if strcmpi(OCS.BPM{i}.FamilyName, 'BPMx') || ismemberof(OCS.BPM{i}.FamilyName, 'BPMx') |
---|
467 | if ~isfield(OCS.BPM{i}, 'Dispersion') |
---|
468 | % Dispersion at the correctors in physics units |
---|
469 | [OCS.BPM{i}.Dispersion, Dy] = getdisp(OCS.BPM{i}.FamilyName, OCS.BPM{i}.DeviceList, 'Numeric', OCS.BPM{i}.Units); |
---|
470 | end |
---|
471 | R = [R OCS.BPM{i}.Dispersion(:)']; |
---|
472 | |
---|
473 | HCM = physics2hw(OCS.CM{i}); |
---|
474 | |
---|
475 | |
---|
476 | % Move ALS specific code to setorbitdefault ??? |
---|
477 | if strcmpi(getfamilydata('Machine'),'ALS') |
---|
478 | % For the ALS, either the HCMCHICANE families need to be included or |
---|
479 | % the chicane "part" of the HCMs needs to be removed. This will remove the chicane. |
---|
480 | Energy = getfamilydata('Energy'); |
---|
481 | |
---|
482 | % Sector 6 |
---|
483 | % Off 1.9 GeV 1.5 GeV |
---|
484 | % HCMCHICANEM(6,1) 80.0 18.0 ? |
---|
485 | % HCMCHICANEM(6,1) 80.0 20.0 ? |
---|
486 | % HCM(6,1) 0.0 18.8 ? |
---|
487 | ihcm = findrowindex([6 1], OCS.CM{i}.DeviceList); |
---|
488 | if length(ihcm) == 1 |
---|
489 | try |
---|
490 | if getsp('HCMCHICANEM',[6 1]) < 70 |
---|
491 | % Assume sector 6 chicane is on |
---|
492 | if Energy == 1.9 |
---|
493 | HCM.Data(ihcm) = HCM.Data(ihcm) - 18.8; |
---|
494 | else |
---|
495 | HCM.Data(ihcm) = HCM.Data(ihcm) - 18.8*1.5/1.9; |
---|
496 | end |
---|
497 | end |
---|
498 | catch |
---|
499 | fprintf('%s\n', lasterr); |
---|
500 | fprintf('Due to an error, the chicane "offset" on HCM(6,1) will not be removed.\n\n'); |
---|
501 | end |
---|
502 | end |
---|
503 | |
---|
504 | % Sector 11 |
---|
505 | % Off 1.9 GeV 1.5 GeV |
---|
506 | % HCMCHICANEM(11,1) 80.0 40.5 52.0 |
---|
507 | % HCMCHICANEM(11,1) 80.0 40.5 52.0 |
---|
508 | % HCM(10,8) 0.0 -17.0 -14.0 |
---|
509 | % HCM(11,1) 0.0 -17.0 -14.0 |
---|
510 | ihcm = findrowindex([10 8], OCS.CM{i}.DeviceList); |
---|
511 | if length(ihcm) == 1 |
---|
512 | try |
---|
513 | if getsp('HCMCHICANEM',[11 1]) < 60 |
---|
514 | % Assume sector 11 chicane is on |
---|
515 | if Energy == 1.9 |
---|
516 | HCM.Data(ihcm) = HCM.Data(ihcm) + 17; |
---|
517 | else |
---|
518 | HCM.Data(ihcm) = HCM.Data(ihcm) + 14; |
---|
519 | end |
---|
520 | end |
---|
521 | catch |
---|
522 | fprintf('%s\n', lasterr); |
---|
523 | fprintf('Problem reading HCMCHICANEM(11,1). The chicane "offset" on HCM(10,8) will not be removed.n\n'); |
---|
524 | end |
---|
525 | end |
---|
526 | ihcm = findrowindex([11 1], OCS.CM{i}.DeviceList); |
---|
527 | if length(ihcm) == 1 |
---|
528 | try |
---|
529 | if getsp('HCMCHICANEM',[11 1]) < 60 |
---|
530 | % Assume sector 11 chicane is on |
---|
531 | if Energy == 1.9 |
---|
532 | HCM.Data(ihcm) = HCM.Data(ihcm) + 17; |
---|
533 | else |
---|
534 | HCM.Data(ihcm) = HCM.Data(ihcm) + 14; |
---|
535 | end |
---|
536 | end |
---|
537 | catch |
---|
538 | fprintf('%s\n', lasterr); |
---|
539 | fprintf('Due to an error, the chicane "offset" on HCM(11,1) will not be removed.n\n'); |
---|
540 | end |
---|
541 | end |
---|
542 | end |
---|
543 | % END ALS only |
---|
544 | |
---|
545 | |
---|
546 | % Assumes 1 HCM family and it's first??? |
---|
547 | DispDotSmatHCM = DispDotSmatHCM + OCS.BPM{i}.Dispersion'*Smat(1:size(OCS.BPM{i}.DeviceList,1),1:1:size(OCS.CM{i}.DeviceList,1))*HCM.Data; |
---|
548 | else |
---|
549 | R = [R zeros(1,size(OCS.BPM{i}.DeviceList,1))]; |
---|
550 | end |
---|
551 | end |
---|
552 | % [OCS.BPM{1}.Dispersion, Dy] = getdisp(OCS.BPM{1}.FamilyName, OCS.BPM{1}.DeviceList, 'Numeric', OCS.BPM{1}.Units); |
---|
553 | % RR = [OCS.BPM{1}.Dispersion(:); zeros(size(OCS.BPM{2}.DeviceList,1),1)]'; |
---|
554 | |
---|
555 | %r = 0; |
---|
556 | r = -1*DispDotSmatHCM; % Not sure if this should be divided by 2 |
---|
557 | R = [R*Smat(:,1:end-1) 0]; |
---|
558 | |
---|
559 | |
---|
560 | % Projection onto the columns of Smat*V (or U) |
---|
561 | % Since the correctors are V*b, the restriction in corrector strength is R*V |
---|
562 | X = Smat * V(:,OCS.SVDIndex); |
---|
563 | R = R * V(:,OCS.SVDIndex); |
---|
564 | b = inv(X'*X)*X' * (BPMWeight .* (GoalOrbitVec - StartOrbitVec)); |
---|
565 | br = b - inv(X'*X)*R'*inv(R*inv(X'*X)*R')*(R*b-r); |
---|
566 | Delcm = V(:,OCS.SVDIndex) * br; |
---|
567 | |
---|
568 | %DelcmNoR = V(:,OCS.SVDIndex) * b; |
---|
569 | %fprintf(' dRF=%g dRF(NoR)=%g\n', RFWeight * Delcm(end), RFWeight * DelcmNoR(end)); |
---|
570 | |
---|
571 | elseif OCS.FitRF == 5 |
---|
572 | % Constrained L.S. with SVD |
---|
573 | % Constraint: dot(HCM(Dispersion), dHCM) = 0 |
---|
574 | R = []; |
---|
575 | for i = 1:length(OCS.CM) |
---|
576 | if ismemberof(OCS.CM{i}.FamilyName, 'HCM') |
---|
577 | % Assume CM{i} is the same plane as BPM{i} |
---|
578 | if ~isfield(OCS.BPM{i}, 'Dispersion') |
---|
579 | % Dispersion at the horizontal BPMs |
---|
580 | [OCS.BPM{i}.Dispersion, Dy] = getdisp(OCS.BPM{i}.FamilyName, OCS.BPM{i}.DeviceList, 'Numeric', OCS.BPM{i}.Units); |
---|
581 | |
---|
582 | % Find the corrector pattern of the dispersion orbit |
---|
583 | % |
---|
584 | % OCS.SVDIndex ???? |
---|
585 | % Smat??? make an array |
---|
586 | % Why is Rhcm so different from Rbpmx? |
---|
587 | |
---|
588 | % Assumes 1 HCM family and it's first??? |
---|
589 | SmatNoEta = Smat(1:size(OCS.BPM{i}.DeviceList,1),1:size(OCS.CM{i}.DeviceList,1)); |
---|
590 | [UU, SS, VV] = svd(SmatNoEta, 'econ'); |
---|
591 | SS = diag(SS); |
---|
592 | |
---|
593 | X = SmatNoEta * VV(:,OCS.SVDIndex); |
---|
594 | b = inv(X'*X)*X' * OCS.BPM{i}.Dispersion; |
---|
595 | OCS.BPM{i}.DispersionCorrectors = VV(:,OCS.SVDIndex) * b; |
---|
596 | end |
---|
597 | R = [R OCS.BPM{i}.DispersionCorrectors(:)']; |
---|
598 | else |
---|
599 | R = [R zeros(1,size(OCS.CM{i}.DeviceList,1))]; |
---|
600 | end |
---|
601 | end |
---|
602 | |
---|
603 | r = 0; % Should it be an absolute conditional ??? |
---|
604 | R = [R 0]; |
---|
605 | |
---|
606 | |
---|
607 | % Projection onto the columns of Smat*V (or U) |
---|
608 | % Since the correctors are V*b, the restriction in corrector strength is R*V |
---|
609 | X = Smat * V(:,OCS.SVDIndex); |
---|
610 | R = R * V(:,OCS.SVDIndex); |
---|
611 | b = inv(X'*X)*X' * (BPMWeight .* (GoalOrbitVec - StartOrbitVec)); |
---|
612 | br = b - inv(X'*X)*R'*inv(R*inv(X'*X)*R')*(R*b-r); |
---|
613 | Delcm = V(:,OCS.SVDIndex) * br; |
---|
614 | |
---|
615 | %DelcmNoR = V(:,OCS.SVDIndex) * b; |
---|
616 | %fprintf(' dRF=%g dRF(NoR)=%g\n', RFWeight * Delcm(end), RFWeight * DelcmNoR(end)); |
---|
617 | |
---|
618 | else |
---|
619 | |
---|
620 | % L.S. with SVD algorithm |
---|
621 | % The V matrix columns are the singular vectors in the corrector magnet space |
---|
622 | % The U matrix columns are the singular vectors in the BPM space |
---|
623 | % Smat = U*S*V' where U'*U=I and V*V'=I |
---|
624 | % |
---|
625 | % b is the coef. of the projection onto the columns of Smat*V(:,Ivec) - the corrector space (same space as spanned by U) |
---|
626 | % Sometimes it's interesting to look at the size of these coefficients with singular value number. |
---|
627 | b = U(:,OCS.SVDIndex)' * (BPMWeight .* (GoalOrbitVec - StartOrbitVec)); |
---|
628 | b = diag(S(OCS.SVDIndex).^(-1)) * b; |
---|
629 | |
---|
630 | % Convert the b vector back to coefficents of response matrix |
---|
631 | Delcm = V(:,OCS.SVDIndex) * b; |
---|
632 | end |
---|
633 | |
---|
634 | |
---|
635 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
636 | % Remove Weights for the Correction % |
---|
637 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
---|
638 | % Separate the RF from the correctors |
---|
639 | if OCS.FitRF |
---|
640 | % RF frequency is the last actuator |
---|
641 | OCS.DeltaRF = RFWeight * Delcm(end); |
---|
642 | if strcmpi(getfamilydata('SubMachine'), 'StorageRing') |
---|
643 | % Begin Add by Laurent % To be done with variables |
---|
644 | % test for avoiding to move too often RF frequency |
---|
645 | %fprintf('Prog %e \n',DelRF); |
---|
646 | if abs(OCS.DeltaRF) < DeltaRFmin % minimum RF is .1 Hz |
---|
647 | OCS.DeltaRF = 0; |
---|
648 | end |
---|
649 | if abs(OCS.DeltaRF) > DeltaRFmax % maximum is 100 Hz |
---|
650 | error('RF change too large DeltaRF = %f Hz (> %f Hz) \nRF frequency shift has to be fixed first by hand.\nSee expert steprf command', ... |
---|
651 | hw2physics('RF','Setpoint',OCS.DeltaRF), hw2physics('RF','Setpoint',DeltaRFmax)); |
---|
652 | end |
---|
653 | end |
---|
654 | Delcm = Delcm(1:end-1); |
---|
655 | end |
---|
656 | |
---|
657 | % Remove column weights from Delcm |
---|
658 | Delcm = Delcm .* CMWeight; |
---|
659 | |
---|
660 | |
---|
661 | % Predict Orbit |
---|
662 | if OCS.FitRF |
---|
663 | OrbitDeltaTotal = [SmatNoWeights OCS.Eta] * [Delcm; OCS.DeltaRF]; |
---|
664 | else |
---|
665 | OrbitDeltaTotal = SmatNoWeights * Delcm; |
---|
666 | end |
---|
667 | for j = 1:length(OCS.BPM) |
---|
668 | N = length(OCS.BPM{j}.Data); |
---|
669 | OCS.BPM{j}.PredictedOrbitDelta = OrbitDeltaTotal(1:N); |
---|
670 | OrbitDeltaTotal(1:N) = []; |
---|
671 | end |
---|
672 | |
---|
673 | |
---|
674 | % Don't put the Delcm into OCS.CM so this function can get |
---|
675 | % called multiple times before actually correcting the orbit |
---|
676 | for i = 1:length(OCS.CM) |
---|
677 | N = length(OCS.CM{i}.Data); |
---|
678 | %OCS.CM{i}.Data = OCS.CM{i}.Data + Delcm(1:N); |
---|
679 | OCS.CM{i}.Delta = Delcm(1:N); |
---|
680 | Delcm(1:N) = []; |
---|
681 | |
---|
682 | OCS.CMWeight{i} = CMWeight(1:N); |
---|
683 | CMWeight(1:N) = []; |
---|
684 | end |
---|
685 | |
---|
686 | |
---|
687 | % Remove the cell array if the length is one |
---|
688 | if NoCellBPMFlag |
---|
689 | OCS.BPM = OCS.BPM{1}; |
---|
690 | OCS.GoalOrbit = OCS.GoalOrbit{1}; |
---|
691 | OCS.BPMWeight = OCS.BPMWeight{1}; |
---|
692 | end |
---|
693 | if NoCellCMFlag |
---|
694 | OCS.CM = OCS.CM{1}; |
---|
695 | OCS.CMWeight = OCS.CMWeight{1}; |
---|
696 | end |
---|
697 | |
---|
698 | |
---|
699 | % Output statistics??? |
---|
700 | % 1. Correctable orbit |
---|
701 | % 2. Not correctable orbit |
---|
702 | % 3. Error propagation |
---|
703 | |
---|
704 | |
---|