function ChiSquare = calclocochi2(LocoModel, LocoMeasData, BPMData, CMData, FitParameters, LocoFlags, RINGData) %CALCLOCOCHI2 - Calculate the contribution to chi^2 of each fit parameter in LOCO % ChiSquare = calclocochi2(LocoModel, LocoMeasData, BPMData, CMData, FitParameters, LocoFlags, RINGData) % ChiSquare = calclocochi2(LOCOFileName) % Input #1 can be a LOCO file if ischar(LocoModel) load(LocoModel); end % Get the flags LocoParams = FitParameters.Params; LocoValues = FitParameters.Values; LocoDeltas = FitParameters.Deltas; SVmethod = LocoFlags.SVmethod; AutoCorrectDeltaFlag = LocoFlags.AutoCorrectDelta; CouplingFlag = LocoFlags.Coupling; NormalizeFlag = LocoFlags.Normalize; OutlierFactor = LocoFlags.OutlierFactor; % UNITS CONVERSIONS (to be combatible with tracking code) % Convert corrector kicks used in the response matrix to radians CMData.HCMKicks = CMData.HCMKicks(:) / 1000; % milliradian to radians (column vector) CMData.VCMKicks = CMData.VCMKicks(:) / 1000; % milliradian to radians (column vector) % Convert the measured response matrix to meters LocoMeasData.M = LocoMeasData.M / 1000; % Convert the BPMSTD to meters and make the same size as a response matrix LocoMeasData.BPMSTD = LocoMeasData.BPMSTD / 1000; % mm to meters Mstd = LocoMeasData.BPMSTD * ones(1,size(LocoMeasData.M,2)); % Convert orbit for "dispersion" in meters in column vector format LocoMeasData.Eta = LocoMeasData.Eta(:) / 1000; % mm to meters % END UNITS CONVERTSION % Use the entire family of BPMs in the model response matrix, then index later (not much difference computationally) BPMIndexShortX = BPMData.HBPMIndex(BPMData.HBPMGoodDataIndex); BPMIndexShortY = length(BPMData.BPMIndex)+BPMData.VBPMIndex(BPMData.VBPMGoodDataIndex); BPMIndexShort = [BPMIndexShortX(:)' BPMIndexShortY(:)']; NHBPM = length(BPMData.HBPMGoodDataIndex); NVBPM = length(BPMData.VBPMGoodDataIndex); NBPM = NHBPM + NVBPM; NHCM = length(CMData.HCMGoodDataIndex); NVCM = length(CMData.VCMGoodDataIndex); % Remove unwanted data from the Mmeas and Mstd Mmeas = LocoMeasData.M([BPMData.HBPMGoodDataIndex length(BPMData.HBPMIndex)+BPMData.VBPMGoodDataIndex], [CMData.HCMGoodDataIndex length(CMData.HCMIndex)+CMData.VCMGoodDataIndex]); Mstd = Mstd([BPMData.HBPMGoodDataIndex length(BPMData.HBPMIndex)+BPMData.VBPMGoodDataIndex], [CMData.HCMGoodDataIndex length(CMData.HCMIndex)+CMData.VCMGoodDataIndex]); % If including dispersion then Mstd and Mmeas must include disperion term if strcmpi((LocoFlags.Dispersion),'yes') Xstd = LocoMeasData.BPMSTD(BPMData.HBPMGoodDataIndex); Ystd = LocoMeasData.BPMSTD(length(BPMData.HBPMIndex)+BPMData.VBPMGoodDataIndex); if isempty(LocoMeasData.Eta) error('Measured dispersion (LocoMeasData.Eta) can not be empty when including dispersion'); end EtaX = LocoMeasData.Eta(BPMData.HBPMGoodDataIndex); EtaY = LocoMeasData.Eta(length(BPMData.HBPMIndex)+BPMData.VBPMGoodDataIndex); LocoFlags.HorizontalDispersionWeight = abs(LocoFlags.HorizontalDispersionWeight); LocoFlags.VerticalDispersionWeight = abs(LocoFlags.VerticalDispersionWeight); % Should remove the dispersion if both weights are zero if LocoFlags.HorizontalDispersionWeight == 0 LocoFlags.HorizontalDispersionWeight = eps; end if LocoFlags.VerticalDispersionWeight == 0 LocoFlags.VerticalDispersionWeight = eps; end % Weight the dispersion Mstd = [Mstd [Xstd/LocoFlags.HorizontalDispersionWeight; Ystd/LocoFlags.VerticalDispersionWeight]]; Mmeas = [Mmeas [EtaX; EtaY]]; end % Convert to a column vector Mstd = Mstd(:); Mmeas = Mmeas(:); % Build the corrector magnet data structures to be used with locoresponsematrix CMDataRM.FamName = CMData.FamName; CMDataRM.HCMIndex = CMData.HCMIndex(CMData.HCMGoodDataIndex); CMDataRM.VCMIndex = CMData.VCMIndex(CMData.VCMGoodDataIndex); CMDataRM.HCMKicks = CMData.HCMKicks(CMData.HCMGoodDataIndex); CMDataRM.VCMKicks = CMData.VCMKicks(CMData.VCMGoodDataIndex); CMDataRM.HCMCoupling = CMData.HCMCoupling(CMData.HCMGoodDataIndex); CMDataRM.VCMCoupling = CMData.VCMCoupling(CMData.VCMGoodDataIndex); CMDataRM.HCMEnergyShift = CMData.HCMEnergyShift(CMData.HCMGoodDataIndex); CMDataRM.VCMEnergyShift = CMData.VCMEnergyShift(CMData.VCMGoodDataIndex); % Compute the new model response matrix with dispersion for saving %fprintf(' Computing final response matrix (after fit) (%s, %s) ... ', LocoFlags.ResponseMatrixCalculator, LocoFlags.ClosedOrbitType); tic CMDataRM.HCMKicks = CMData.HCMKicks(CMData.HCMGoodDataIndex); CMDataRM.VCMKicks = CMData.VCMKicks(CMData.VCMGoodDataIndex); CMDataRM.HCMCoupling = CMData.HCMCoupling(CMData.HCMGoodDataIndex); CMDataRM.VCMCoupling = CMData.VCMCoupling(CMData.VCMGoodDataIndex); warning off; lastwarn(''); %modify RINGData LocoValues = LocoValues(:); % Force a column vector for i = 1:length(LocoParams) % Compute the response matrix with the parameter change RINGData = locosetlatticeparam(RINGData, LocoParams{i}, LocoValues(i)); end if isempty(FitParameters.DeltaRF) Mmodel = locoresponsematrix(RINGData, BPMData, CMDataRM, LocoFlags); else Mmodel = locoresponsematrix(RINGData, BPMData, CMDataRM, LocoFlags, 'RF', FitParameters.DeltaRF); end warning on; %fprintf('%f seconds. \n',toc); % if ~isempty(lastwarn) % fprintf('\n Warning computing the final response matrix:\n %s\n', lastwarn); % fprintf( ' Check the final values of the fits to make sure they are in a reasonable range for\n'); % fprintf( ' this accelerator. Check the input data and/or reduce the number of singular values.\n\n'); % end % To remove the off-diagonal part of the A matrix find the index vector, iNoCoupling, of rows to keep if strcmpi((CouplingFlag),'no') CF = [ ones(NHBPM,NHCM) zeros(NHBPM,NVCM); zeros(NVBPM,NHCM) ones(NVBPM,NVCM)]; % Keep the dispersion if strcmpi((LocoFlags.Dispersion),'yes') % Keep the horizontal and vertical part of the "dispersion" orbit CF = [CF [2*ones(NHBPM,1); 3*ones(NVBPM,1)]]; % Make zeros to ignor dispersion end CF = CF(:); iNoCoupling = find(CF > 0); % Rows of A to keep when ignoring coupling %iHorizontalDispersion = find(CF == 2); % Rows of A corresponding to horizontal dispersion %iVerticalDispersion = find(CF == 3); % Rows of A corresponding to vertical dispersion clear CF else if strcmpi((LocoFlags.Dispersion),'yes') iNoCoupling = (1:(NVBPM+NHBPM)*(NHCM+NVCM+1))'; else iNoCoupling = (1:(NVBPM+NHBPM)*(NHCM+NVCM))'; end end % Rotate Mmodel and remove BPMs not in the measured response matrix C11 = ones(length(BPMData.BPMIndex),1); C11(BPMData.HBPMIndex(BPMData.HBPMGoodDataIndex)) = BPMData.HBPMGain(BPMData.HBPMGoodDataIndex); C12 = zeros(length(BPMData.BPMIndex),1); C12(BPMData.HBPMIndex(BPMData.HBPMGoodDataIndex)) = BPMData.HBPMCoupling(BPMData.HBPMGoodDataIndex); C21 = zeros(length(BPMData.BPMIndex),1); C21(BPMData.VBPMIndex(BPMData.VBPMGoodDataIndex)) = BPMData.VBPMCoupling(BPMData.VBPMGoodDataIndex); C22 = ones(length(BPMData.BPMIndex),1); C22(BPMData.VBPMIndex(BPMData.VBPMGoodDataIndex)) = BPMData.VBPMGain(BPMData.VBPMGoodDataIndex); C = [diag(C11) diag(C12) diag(C21) diag(C22)]; clear C11 C12 C21 C22 Mmodel = C * Mmodel; Mmodel = Mmodel(BPMIndexShort, :); % Compute chi-squared based on new model Mmeas = LocoMeasData.M; Mmeas = Mmeas([BPMData.HBPMGoodDataIndex length(BPMData.HBPMIndex)+BPMData.VBPMGoodDataIndex], [CMData.HCMGoodDataIndex length(CMData.HCMIndex)+CMData.VCMGoodDataIndex]); Mstd = LocoMeasData.BPMSTD * ones(1,size(LocoMeasData.M,2)); Mstd = Mstd ([BPMData.HBPMGoodDataIndex length(BPMData.HBPMIndex)+BPMData.VBPMGoodDataIndex], [CMData.HCMGoodDataIndex length(CMData.HCMIndex)+CMData.VCMGoodDataIndex]); Xstd = LocoMeasData.BPMSTD(BPMData.HBPMGoodDataIndex); Ystd = LocoMeasData.BPMSTD(length(BPMData.HBPMIndex)+BPMData.VBPMGoodDataIndex); % When using the fixed momentum response matrix calculator, the merit function becomes: % Merit = Mmeas_ij - Mmod_ij - Dp/p_j * eta_i % where eta_i is the measured eta (not the model eta) % This is done by changing Mmodel to (Mmodel_ij + Dp/p_j * eta_i) %if strcmpi((CMData.FitHCMEnergyShift),'yes') | strcmpi((CMData.FitVCMEnergyShift),'yes') if strcmpi((LocoFlags.ClosedOrbitType), 'fixedmomentum') HCMEnergyShift = CMData.HCMEnergyShift(CMData.HCMGoodDataIndex); VCMEnergyShift = CMData.VCMEnergyShift(CMData.VCMGoodDataIndex); if ~exist('AlphaMCF') AlphaMCF = locomcf(RINGData); EtaXmcf = -AlphaMCF * LocoMeasData.RF * LocoMeasData.Eta(BPMData.HBPMGoodDataIndex) / LocoMeasData.DeltaRF; EtaYmcf = -AlphaMCF * LocoMeasData.RF * LocoMeasData.Eta(length(BPMData.HBPMIndex)+BPMData.VBPMGoodDataIndex) / LocoMeasData.DeltaRF; end for i = 1:length(HCMEnergyShift) Mmodel(:,i) = Mmodel(:,i) + HCMEnergyShift(i) * [EtaXmcf; EtaYmcf]; end for i = 1:length(VCMEnergyShift) Mmodel(:,NHCM+i) = Mmodel(:,NHCM+i) + VCMEnergyShift(i) * [EtaXmcf; EtaYmcf]; end end Mstd = Mstd(:); Mmeas = Mmeas(:); if strcmpi((LocoFlags.Dispersion),'yes') EtaX = LocoMeasData.Eta(BPMData.HBPMGoodDataIndex); EtaY = LocoMeasData.Eta(length(BPMData.HBPMIndex)+BPMData.VBPMGoodDataIndex); Mstd = [Mstd; [Xstd; Ystd]]; Mmeas = [Mmeas; [EtaX; EtaY]]; else if ~isempty(FitParameters.DeltaRF) Mmodel = Mmodel(:,1:end-1); end end Mmodel = Mmodel(:); % Combine the Mmodel outliers and the Eta outliers if strcmpi((LocoFlags.Dispersion),'yes') iOutliers = [LocoModel.OutlierIndex, LocoModel.EtaOutlierIndex + ((NHBPM+NVBPM)*(NHCM+NVCM))]; else iOutliers = LocoModel.OutlierIndex; end % Outliers are referenced to the coupled model % Mark the outliers with NaN Mmeas(iOutliers) = NaN; Mmodel(iOutliers) = NaN; Mstd(iOutliers) = NaN; % Remove coupling rows if strcmpi((CouplingFlag),'no') Mmodel = Mmodel(iNoCoupling); Mmeas = Mmeas(iNoCoupling); Mstd = Mstd(iNoCoupling); end % Remove the outliers Mmeas(find(isnan(Mmeas))) = []; Mmodel(find(isnan(Mmodel))) = []; Mstd(find(isnan(Mstd))) = []; NumberOfFitParameters = 0; %count the number of fit paramters % Horizontal BPM gains if strcmpi((BPMData.FitGains),'yes') NumberOfFitParameters = NumberOfFitParameters + length(BPMData.HBPMGoodDataIndex); end % Horizontal BPM coupling if strcmpi((BPMData.FitCoupling),'yes') NumberOfFitParameters = NumberOfFitParameters + length(BPMData.HBPMGoodDataIndex); end % Vertical BPM coupling if strcmpi((BPMData.FitCoupling),'yes') NumberOfFitParameters = NumberOfFitParameters + length(BPMData.VBPMGoodDataIndex); end % Vertical BPM gains if strcmpi((BPMData.FitGains),'yes') NumberOfFitParameters = NumberOfFitParameters + length(BPMData.VBPMGoodDataIndex); end % Corrector magnet gains if strcmpi((CMData.FitKicks),'yes') NumberOfFitParameters = NumberOfFitParameters + length(CMData.HCMGoodDataIndex); end if strcmpi((CMData.FitKicks),'yes') NumberOfFitParameters = NumberOfFitParameters + length(CMData.VCMGoodDataIndex); end % Corrector magnet coupling if strcmpi((CMData.FitCoupling),'yes') NumberOfFitParameters = NumberOfFitParameters + length(CMData.HCMGoodDataIndex); end if strcmpi((CMData.FitCoupling),'yes') NumberOfFitParameters = NumberOfFitParameters + length(CMData.VCMGoodDataIndex); end % Corrector magnet energy shifts if strcmpi((CMData.FitHCMEnergyShift),'yes') NumberOfFitParameters = NumberOfFitParameters + length(CMData.HCMGoodDataIndex); end if strcmpi((CMData.FitVCMEnergyShift),'yes') NumberOfFitParameters = NumberOfFitParameters + length(CMData.VCMGoodDataIndex); end % RF Frequency parameter fit if strcmpi((FitParameters.FitRFFrequency),'yes') NumberOfFitParameters = NumberOfFitParameters + 1; end % The rest of the parameter fits NumberOfFitParameters = NumberOfFitParameters + length(FitParameters.Values); %ChiSquare = sum(((Mmeas - Mmodel) ./ Mstd) .^ 2) / length(Mstd); ChiSquare = sum(((Mmeas - Mmodel) ./ Mstd) .^ 2) / (length(Mstd)-NumberOfFitParameters); % mean e'*e = sigma*(n-k) % fprintf(' Chi-square/D.O.F. = %f (N=%d, K=%d) (computed from final response matrix)\n\n', ChiSquare, length(Mstd), NumberOfFitParameters); LocoModel.ChiSquare = ChiSquare; % Unit conversions (back to LOCO units) CMData.HCMKicks = 1000*CMData.HCMKicks; % radian to milliradians CMData.VCMKicks = 1000*CMData.VCMKicks; % radian to milliradians CMData.HCMKicksSTD = 1000*CMData.HCMKicksSTD; % radian to milliradians CMData.VCMKicksSTD = 1000*CMData.VCMKicksSTD; % radian to milliradians LocoModel.M = 1000*LocoModel.M; % meters to millimeters LocoModel.Eta = 1000*LocoModel.Eta; % meters to millimeters