Added a generic fit progress viewer and then scripts to run atom number and temperature extraction.
This commit is contained in:
parent
ad048ac2ef
commit
839d84035e
234
Data-Analyzer/+Analyzer/runFitProgressViewer.m
Normal file
234
Data-Analyzer/+Analyzer/runFitProgressViewer.m
Normal file
@ -0,0 +1,234 @@
|
||||
function results = runFitProgressViewer(od_imgs, model, quantities, plotConfig, extraParams)
|
||||
%% runFitProgressViewer
|
||||
% Author: Karthik
|
||||
% Date: 2025-09-12
|
||||
% Version: 1.0
|
||||
%
|
||||
% Description:
|
||||
% Generic batch fit viewer for 2D OD images using any model that
|
||||
% implements guess() and fit(). Dynamically updates image, fit, residual,
|
||||
% and optional bottom-row metrics (atom number, condensate fraction, temperature)
|
||||
%
|
||||
% Inputs:
|
||||
% od_imgs : cell array of 2D images to fit
|
||||
% model : object implementing guess() and fit()
|
||||
% quantities : cell array of field names to compute/display
|
||||
% plotConfig : struct controlling figure appearance and bottom row plots
|
||||
% .fontName, .fontSize, .fontWeight, .colormapName, .scatterLineSpec
|
||||
% .bottomRowLabels : corresponding y-axis labels
|
||||
% .bottomRowUnits : scaling for display
|
||||
% .bottomRowTitles : optional subplot titles (default = labels)
|
||||
% extraParams : struct of optional parameters for the model (e.g., ToF)
|
||||
%
|
||||
% Notes:
|
||||
% Optional notes, references.
|
||||
|
||||
arguments
|
||||
od_imgs cell
|
||||
model
|
||||
quantities cell
|
||||
plotConfig struct = struct()
|
||||
extraParams struct = struct()
|
||||
end
|
||||
|
||||
%% --- Default configuration ---
|
||||
defaults = struct(...
|
||||
'fontName', 'Bahnschrift', ...
|
||||
'fontSize', 16, ...
|
||||
'fontWeight', 'bold', ...
|
||||
'colormapName', 'sky', ...
|
||||
'scatterLineSpec', '-o', ...
|
||||
'bottomRowFields', quantities, ... % <- derived from user input
|
||||
'bottomRowLabels', {'Atom Number','# Condensed','Temp'}, ...
|
||||
'bottomRowUnits', [1,1,1] ...
|
||||
);
|
||||
|
||||
%% --- Merge user config with defaults ---
|
||||
config = struct();
|
||||
flds = fieldnames(plotConfig);
|
||||
for k = 1:numel(flds)
|
||||
config.(flds{k}) = plotConfig.(flds{k});
|
||||
end
|
||||
|
||||
% Ensure bottomRowFields is set from quantities if not provided
|
||||
if ~isfield(config,'bottomRowFields') || isempty(config.bottomRowFields)
|
||||
config.bottomRowFields = quantities;
|
||||
end
|
||||
|
||||
numImages = numel(od_imgs);
|
||||
|
||||
fprintf('\n[INFO] Starting processing of %d images...\n', numImages);
|
||||
|
||||
%% --- Preallocate results struct ---
|
||||
results = repmat(struct('imageIndex',[],'fitResult',[],'gof',[], ...
|
||||
'params',[],'fitData',[],'residuals',[],'rsquare',[],'status','Not processed'), ...
|
||||
numImages,1);
|
||||
|
||||
for i = 1:numImages
|
||||
results(i).imageIndex = i;
|
||||
end
|
||||
|
||||
%% --- Create or reuse figure ---
|
||||
hFig = findobj('Type','figure','Tag','FitProgressViewer');
|
||||
if isempty(hFig)
|
||||
hFig = figure('Position',[100,100,1450,850], ...
|
||||
'NumberTitle','off', ...
|
||||
'Name','Fit Progress Viewer', ...
|
||||
'Tag','FitProgressViewer');
|
||||
else
|
||||
figure(hFig); clf;
|
||||
end
|
||||
t = tiledlayout(2,3,'TileSpacing','compact','Padding','compact');
|
||||
|
||||
%% --- Pre-create image/fit/residual axes ---
|
||||
[axOriginal,hOriginal] = createImageAxis(nexttile(1), 'OD Image');
|
||||
[axFit,hFit] = createImageAxis(nexttile(2), 'Fit');
|
||||
[axResidual,hResidual] = createImageAxis(nexttile(3), 'Residual');
|
||||
|
||||
%% --- Pre-create bottom-row axes ---
|
||||
axBottom = gobjects(numel(config.bottomRowFields),1);
|
||||
scatterBottom = gobjects(numel(config.bottomRowFields),1);
|
||||
for k = 1:numel(config.bottomRowFields)
|
||||
axBottom(k) = nexttile(3+k);
|
||||
scatterBottom(k) = plot(axBottom(k), nan, nan, config.scatterLineSpec);
|
||||
hold(axBottom(k),'on'); grid(axBottom(k),'on');
|
||||
xlabel(axBottom(k),'Image Index','FontName',config.fontName);
|
||||
ylabel(axBottom(k),config.bottomRowLabels{k},'FontName',config.fontName);
|
||||
|
||||
% Use bottomRowTitles if provided, else fallback to labels
|
||||
if isfield(config,'bottomRowTitles') && numel(config.bottomRowTitles) >= k
|
||||
titleStr = config.bottomRowTitles{k};
|
||||
else
|
||||
titleStr = config.bottomRowLabels{k};
|
||||
end
|
||||
title(axBottom(k), titleStr, 'FontName',config.fontName, ...
|
||||
'FontSize',config.fontSize, 'FontWeight',config.fontWeight);
|
||||
end
|
||||
|
||||
%% --- Apply consistent font formatting ---
|
||||
allAxes = [axOriginal, axFit, axResidual, axBottom(:)'];
|
||||
for ax = allAxes, set(ax,'FontSize',config.fontSize,'FontName',config.fontName); end
|
||||
|
||||
%% --- Main batch loop ---
|
||||
for i = 1:numImages
|
||||
currentImg = od_imgs{i};
|
||||
if isempty(currentImg) || ~isnumeric(currentImg) || all(isnan(currentImg(:)))
|
||||
warning('Image %d empty or invalid. Skipping',i);
|
||||
results(i).status = 'Invalid image'; continue;
|
||||
end
|
||||
|
||||
[ny,nx] = size(currentImg); x = 1:nx; y = 1:ny;
|
||||
|
||||
%% --- Model guess and fit ---
|
||||
params = model.guess(currentImg,x,y);
|
||||
|
||||
if isempty(fieldnames(extraParams))
|
||||
[fitResult,gof] = model.fit(currentImg,x,y,'params',params);
|
||||
else
|
||||
args = reshape([fieldnames(extraParams)'; struct2cell(extraParams)'],1,[]);
|
||||
[fitResult,gof] = model.fit(currentImg,x,y,'params',params,args{:});
|
||||
end
|
||||
|
||||
[X,Y] = meshgrid(x,y);
|
||||
xyData = [X(:),Y(:)];
|
||||
fitData = reshape(fitResult(xyData), size(currentImg));
|
||||
residuals = currentImg - fitData;
|
||||
|
||||
%% --- Store results ---
|
||||
results(i).fitResult = fitResult;
|
||||
results(i).gof = gof;
|
||||
results(i).params = params;
|
||||
results(i).fitData = fitData;
|
||||
results(i).residuals = residuals;
|
||||
results(i).rsquare = gof.rsquare;
|
||||
results(i).status = 'Success';
|
||||
|
||||
%% --- Compute requested bottom-row fields only ---
|
||||
for k = 1:numel(config.bottomRowFields)
|
||||
fieldName = config.bottomRowFields{k};
|
||||
switch fieldName
|
||||
case 'atom_number'
|
||||
if ismethod(model,'return_atom_number')
|
||||
atomStruct = model.return_atom_number(X,Y,false);
|
||||
results(i).atom_number = atomStruct.N_bec;
|
||||
end
|
||||
case 'condensate_fraction'
|
||||
if isprop(model,'cond_frac')
|
||||
results(i).condensate_fraction = model.cond_frac;
|
||||
end
|
||||
case 'temperature'
|
||||
if ismethod(model,'return_temperature') && isfield(extraParams,'ToF')
|
||||
results(i).temperature = model.return_temperature(extraParams.ToF,[],false);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
%% --- Update plots dynamically ---
|
||||
updatePlots(currentImg, fitData, residuals, i);
|
||||
end
|
||||
|
||||
%% --- Display mean ± SEM after full batch loop (raw values) ---
|
||||
for k = 1:numel(config.bottomRowFields)
|
||||
fieldName = config.bottomRowFields{k};
|
||||
|
||||
% Collect raw values (no scaling)
|
||||
vals = nan(numImages,1);
|
||||
for i = 1:numImages
|
||||
if isfield(results(i), fieldName)
|
||||
vals(i) = results(i).(fieldName); % RAW, unscaled
|
||||
end
|
||||
end
|
||||
|
||||
meanVal = mean(vals,'omitnan');
|
||||
semVal = std(vals,'omitnan')/sqrt(sum(~isnan(vals)));
|
||||
|
||||
% Place in bottom-right corner using normalized axes coordinates
|
||||
ax = axBottom(k);
|
||||
str = sprintf('%.2e ± %.2e', meanVal, semVal);
|
||||
|
||||
text(ax, 0.98, 0.02, str, ...
|
||||
'Units','normalized', ...
|
||||
'HorizontalAlignment','right', 'VerticalAlignment','bottom', ...
|
||||
'FontName', config.fontName, ...
|
||||
'FontSize', config.fontSize, ...
|
||||
'FontWeight', config.fontWeight, ...
|
||||
'BackgroundColor', 'w', ... % white box
|
||||
'Margin', 4, ... % padding inside box
|
||||
'EdgeColor', 'k'); % black border
|
||||
end
|
||||
|
||||
fprintf('\n[INFO] Processing complete.\n');
|
||||
|
||||
%% --- Nested functions ---
|
||||
function [ax,hImg] = createImageAxis(parentTile, titleStr)
|
||||
ax = parentTile;
|
||||
hImg = imagesc(ax, nan); axis(ax,'equal','tight');
|
||||
colormap(ax, config.colormapName); colorbar(ax);
|
||||
title(ax, titleStr, 'FontName', config.fontName, ...
|
||||
'FontSize', config.fontSize, 'FontWeight', config.fontWeight);
|
||||
end
|
||||
|
||||
function updatePlots(img, fitImg, residImg, idx)
|
||||
% Update main image axes
|
||||
set(hOriginal, 'CData', img);
|
||||
set(hFit, 'CData', fitImg);
|
||||
set(hResidual, 'CData', residImg);
|
||||
|
||||
% Update bottom-row plots
|
||||
for k = 1:numel(config.bottomRowFields)
|
||||
fieldName = config.bottomRowFields{k};
|
||||
val = NaN;
|
||||
if isfield(results(idx), fieldName)
|
||||
val = results(idx).(fieldName) * config.bottomRowUnits(k);
|
||||
end
|
||||
if idx == 1
|
||||
scatterBottom(k).XData = idx;
|
||||
scatterBottom(k).YData = val;
|
||||
else
|
||||
scatterBottom(k).XData = [scatterBottom(k).XData, idx];
|
||||
scatterBottom(k).YData = [scatterBottom(k).YData, val];
|
||||
end
|
||||
end
|
||||
drawnow;
|
||||
end
|
||||
end
|
@ -35,7 +35,6 @@ classdef DensityProfileBEC2DModel < handle
|
||||
% Notes:
|
||||
% - All static methods can be called independently without creating an object.
|
||||
|
||||
|
||||
properties
|
||||
% Conversion factors and default constants
|
||||
fwhm_factor = 2*sqrt(2*log(2)); % FWHM factor for Gaussian
|
||||
@ -137,7 +136,7 @@ classdef DensityProfileBEC2DModel < handle
|
||||
center_pix = obj.calc_cen_pix(thresh);
|
||||
center = obj.center_pix_conv(center_pix, x_1d, y_1d);
|
||||
BEC_width_guess = obj.guess_BEC_width(thresh, center_pix);
|
||||
|
||||
|
||||
if obj.is_debug
|
||||
figure;
|
||||
imagesc(x_1d, y_1d, thresh');
|
||||
@ -153,11 +152,17 @@ classdef DensityProfileBEC2DModel < handle
|
||||
|
||||
% 1D slicing along the shorter axis
|
||||
if BEC_width_guess(1) < BEC_width_guess(2)
|
||||
if obj.is_debug
|
||||
disp('x-axis is shorter, performing 1D fit along x-axis');
|
||||
end
|
||||
s_width_ind = 1;
|
||||
x_fit = x_1d;
|
||||
slice_range = round(center_pix(2) - BEC_width_guess(2)/2) : round(center_pix(2) + BEC_width_guess(2)/2);
|
||||
X_guess = sum(data_2d(:, slice_range), 2) / length(slice_range);
|
||||
else
|
||||
if obj.is_debug
|
||||
disp('y-axis is shorter, performing 1D fit along y-axis');
|
||||
end
|
||||
s_width_ind = 2;
|
||||
x_fit = y_1d;
|
||||
slice_range = round(center_pix(1) - BEC_width_guess(1)/2) : round(center_pix(1) + BEC_width_guess(1)/2);
|
||||
@ -165,7 +170,7 @@ classdef DensityProfileBEC2DModel < handle
|
||||
end
|
||||
|
||||
max_val = max(X_guess);
|
||||
|
||||
|
||||
% Construct initial parameter struct
|
||||
params_1d = struct();
|
||||
params_1d.x0_bec.value = center(s_width_ind);
|
||||
@ -184,20 +189,27 @@ classdef DensityProfileBEC2DModel < handle
|
||||
params_1d.amp_th.min = 0;
|
||||
params_1d.amp_th.max = 1.3 * max_val;
|
||||
|
||||
% params_1d.deltax.value = 3 * BEC_width_guess(s_width_ind);
|
||||
% params_1d.deltax.min = 0;
|
||||
% params_1d.deltax.max = max(x_width, y_width);
|
||||
|
||||
params_1d.sigma_bec.value = BEC_width_guess(s_width_ind) / 1.22;
|
||||
params_1d.sigma_bec.min = 0;
|
||||
params_1d.sigma_bec.max = 2 * BEC_width_guess(s_width_ind);
|
||||
|
||||
% params_1d.sigma_th.value = 3 * BEC_width_guess(1);
|
||||
params_1d.sigma_th.value = 0.632 * params_1d.sigma_bec.value + 0.518 * 3 * BEC_width_guess(s_width_ind);
|
||||
params_1d.sigma_th.min = 0;
|
||||
params_1d.sigma_th.max = 3 * (0.632 * params_1d.sigma_bec.value + 0.518 * 3 * BEC_width_guess(s_width_ind));
|
||||
|
||||
% params_1d.sigma_th.expr = '0.632*sigma_bec + 0.518*deltax';
|
||||
|
||||
% Perform 1D bimodal fit
|
||||
[fitResult_1d, gof_1d] = fit_1d_bimodal(x_fit, X_guess, params_1d);
|
||||
|
||||
% Extract fit coefficients
|
||||
bval_1d = coeffvalues(fitResult_1d);
|
||||
paramNames_1d = coeffnames(fitResult_1d);
|
||||
|
||||
for i = 1:length(paramNames_1d)
|
||||
bval_1d_struct.(paramNames_1d{i}) = bval_1d(i);
|
||||
end
|
||||
@ -219,151 +231,373 @@ classdef DensityProfileBEC2DModel < handle
|
||||
ylabel('Intensity');
|
||||
end
|
||||
|
||||
% Final parameter preparation (pre_check logic omitted for brevity)
|
||||
params = params_1d;
|
||||
% Scale amplitudes
|
||||
blurred_data = imgaussfilt(data_2d, 1);
|
||||
amp_conv_1d_2d = max(blurred_data(:)) / (bval_1d_struct.amp_bec + bval_1d_struct.amp_th);
|
||||
max_val = max(data_2d(:));
|
||||
|
||||
% Create parameter struct
|
||||
params = struct();
|
||||
|
||||
% Pre-check: decide if image is pure BEC or pure thermal cloud based on 1D fit result
|
||||
if bval_1d_struct.amp_th / bval_1d_struct.amp_bec > 7 && obj.pre_check
|
||||
if obj.is_debug
|
||||
disp('Image seems to be pure thermal cloud (based on 1D fit amplitudes)');
|
||||
end
|
||||
|
||||
params.([obj.prefix 'amp_bec']).value = 0;
|
||||
params.([obj.prefix 'amp_bec']).vary = false;
|
||||
|
||||
params.([obj.prefix 'amp_th']).value = amp_conv_1d_2d * bval_1d_struct.amp_th;
|
||||
params.([obj.prefix 'amp_th']).max = 1.3 * max_val;
|
||||
params.([obj.prefix 'amp_th']).vary = true;
|
||||
|
||||
params.([obj.prefix 'x0_bec']).value = 1;
|
||||
params.([obj.prefix 'x0_bec']).vary = false;
|
||||
|
||||
params.([obj.prefix 'y0_bec']).value = 1;
|
||||
params.([obj.prefix 'y0_bec']).vary = false;
|
||||
|
||||
params.([obj.prefix 'x0_th']).value = center(1);
|
||||
params.([obj.prefix 'x0_th']).min = center(1) - 10;
|
||||
params.([obj.prefix 'x0_th']).max = center(1) + 10;
|
||||
params.([obj.prefix 'x0_th']).vary = true;
|
||||
|
||||
params.([obj.prefix 'y0_th']).value = center(2);
|
||||
params.([obj.prefix 'y0_th']).min = center(2) - 10;
|
||||
params.([obj.prefix 'y0_th']).max = center(2) + 10;
|
||||
params.([obj.prefix 'y0_th']).vary = true;
|
||||
|
||||
params.([obj.prefix 'sigmax_bec']).value = 1;
|
||||
params.([obj.prefix 'sigmax_bec']).vary = false;
|
||||
|
||||
params.([obj.prefix 'sigmay_bec']).value = 1;
|
||||
params.([obj.prefix 'sigmay_bec']).vary = false;
|
||||
|
||||
params.([obj.prefix 'sigma_th']).value = bval_1d_struct.sigma_th;
|
||||
params.([obj.prefix 'sigma_th']).min = 0;
|
||||
params.([obj.prefix 'sigma_th']).max = max(x_width, y_width);
|
||||
params.([obj.prefix 'sigma_th']).vary = true;
|
||||
|
||||
elseif bval_1d_struct.amp_bec / bval_1d_struct.amp_th > 10 && obj.pre_check
|
||||
if obj.is_debug
|
||||
disp('Image seems to be pure BEC (based on 1D fit amplitudes)');
|
||||
end
|
||||
|
||||
params.([obj.prefix 'amp_bec']).value = amp_conv_1d_2d * bval_1d_struct.amp_bec;
|
||||
params.([obj.prefix 'amp_bec']).max = 1.3 * max_val;
|
||||
params.([obj.prefix 'amp_bec']).vary = true;
|
||||
|
||||
params.([obj.prefix 'amp_th']).value = 0;
|
||||
params.([obj.prefix 'amp_th']).vary = false;
|
||||
|
||||
params.([obj.prefix 'x0_bec']).value = center(1);
|
||||
params.([obj.prefix 'x0_bec']).min = center(1) - 10;
|
||||
params.([obj.prefix 'x0_bec']).max = center(1) + 10;
|
||||
params.([obj.prefix 'x0_bec']).vary = true;
|
||||
|
||||
params.([obj.prefix 'y0_bec']).value = center(2);
|
||||
params.([obj.prefix 'y0_bec']).min = center(2) - 10;
|
||||
params.([obj.prefix 'y0_bec']).max = center(2) + 10;
|
||||
params.([obj.prefix 'y0_bec']).vary = true;
|
||||
|
||||
params.([obj.prefix 'x0_th']).value = 1;
|
||||
params.([obj.prefix 'x0_th']).vary = false;
|
||||
|
||||
params.([obj.prefix 'y0_th']).value = 1;
|
||||
params.([obj.prefix 'y0_th']).vary = false;
|
||||
|
||||
params.([obj.prefix 'sigma_th']).value = 1;
|
||||
params.([obj.prefix 'sigma_th']).vary = false;
|
||||
|
||||
if s_width_ind == 1
|
||||
params.([obj.prefix 'sigmax_bec']).value = bval_1d_struct.sigma_bec;
|
||||
params.([obj.prefix 'sigmax_bec']).max = 2 * BEC_width_guess(1);
|
||||
params.([obj.prefix 'sigmax_bec']).vary = true;
|
||||
|
||||
params.([obj.prefix 'sigmay_bec']).value = BEC_width_guess(2) / 1.22;
|
||||
params.([obj.prefix 'sigmay_bec']).max = 2 * BEC_width_guess(2);
|
||||
params.([obj.prefix 'sigmay_bec']).vary = true;
|
||||
else
|
||||
params.([obj.prefix 'sigmax_bec']).value = BEC_width_guess(1) / 1.22;
|
||||
params.([obj.prefix 'sigmax_bec']).max = 2 * BEC_width_guess(1);
|
||||
params.([obj.prefix 'sigmax_bec']).vary = true;
|
||||
|
||||
params.([obj.prefix 'sigmay_bec']).value = bval_1d_struct.sigma_bec;
|
||||
params.([obj.prefix 'sigmay_bec']).max = 2 * BEC_width_guess(2);
|
||||
params.([obj.prefix 'sigmay_bec']).vary = true;
|
||||
end
|
||||
|
||||
else
|
||||
% Normal bimodal fit parameters
|
||||
params.([obj.prefix 'amp_bec']).value = amp_conv_1d_2d * bval_1d_struct.amp_bec;
|
||||
params.([obj.prefix 'amp_bec']).min = 0;
|
||||
params.([obj.prefix 'amp_bec']).max = 1.3 * max_val;
|
||||
params.([obj.prefix 'amp_bec']).vary = true;
|
||||
|
||||
params.([obj.prefix 'amp_th']).value = amp_conv_1d_2d * bval_1d_struct.amp_th;
|
||||
params.([obj.prefix 'amp_th']).min = 0;
|
||||
params.([obj.prefix 'amp_th']).max = 1.3 * max_val;
|
||||
params.([obj.prefix 'amp_th']).vary = true;
|
||||
|
||||
params.([obj.prefix 'x0_bec']).value = center(1);
|
||||
params.([obj.prefix 'x0_bec']).min = center(1) - 10;
|
||||
params.([obj.prefix 'x0_bec']).max = center(1) + 10;
|
||||
params.([obj.prefix 'x0_bec']).vary = true;
|
||||
|
||||
params.([obj.prefix 'y0_bec']).value = center(2);
|
||||
params.([obj.prefix 'y0_bec']).min = center(2) - 10;
|
||||
params.([obj.prefix 'y0_bec']).max = center(2) + 10;
|
||||
params.([obj.prefix 'y0_bec']).vary = true;
|
||||
|
||||
params.([obj.prefix 'x0_th']).value = center(1);
|
||||
params.([obj.prefix 'x0_th']).min = center(1) - 10;
|
||||
params.([obj.prefix 'x0_th']).max = center(1) + 10;
|
||||
params.([obj.prefix 'x0_th']).vary = true;
|
||||
|
||||
params.([obj.prefix 'y0_th']).value = center(2);
|
||||
params.([obj.prefix 'y0_th']).min = center(2) - 10;
|
||||
params.([obj.prefix 'y0_th']).max = center(2) + 10;
|
||||
params.([obj.prefix 'y0_th']).vary = true;
|
||||
|
||||
params.([obj.prefix 'sigma_th']).value = bval_1d_struct.sigma_th;
|
||||
params.([obj.prefix 'sigma_th']).min = 0;
|
||||
params.([obj.prefix 'sigma_th']).max = max(x_width, y_width);
|
||||
params.([obj.prefix 'sigma_th']).vary = true;
|
||||
|
||||
if s_width_ind == 1
|
||||
params.([obj.prefix 'sigmax_bec']).value = bval_1d_struct.sigma_bec;
|
||||
params.([obj.prefix 'sigmax_bec']).min = 0;
|
||||
params.([obj.prefix 'sigmax_bec']).max = 2 * BEC_width_guess(1);
|
||||
params.([obj.prefix 'sigmax_bec']).vary = true;
|
||||
|
||||
params.([obj.prefix 'sigmay_bec']).value = BEC_width_guess(2) / 1.22;
|
||||
params.([obj.prefix 'sigmay_bec']).min = 0;
|
||||
params.([obj.prefix 'sigmay_bec']).max = 2 * BEC_width_guess(2);
|
||||
params.([obj.prefix 'sigmay_bec']).vary = true;
|
||||
else
|
||||
params.([obj.prefix 'sigmax_bec']).value = BEC_width_guess(1) / 1.22;
|
||||
params.([obj.prefix 'sigmax_bec']).min = 0;
|
||||
params.([obj.prefix 'sigmax_bec']).max = 2 * BEC_width_guess(1);
|
||||
params.([obj.prefix 'sigmax_bec']).vary = true;
|
||||
|
||||
params.([obj.prefix 'sigmay_bec']).value = bval_1d_struct.sigma_bec;
|
||||
params.([obj.prefix 'sigmay_bec']).min = 0;
|
||||
params.([obj.prefix 'sigmay_bec']).max = 2 * BEC_width_guess(2);
|
||||
params.([obj.prefix 'sigmay_bec']).vary = true;
|
||||
end
|
||||
end
|
||||
|
||||
params.([obj.prefix 'rot_angle']).value = rot_angle;
|
||||
params.([obj.prefix 'rot_angle']).min = rot_angle - 30;
|
||||
params.([obj.prefix 'rot_angle']).max = rot_angle + 30;
|
||||
params.([obj.prefix 'rot_angle']).vary = p.Results.vary_rot;
|
||||
|
||||
if obj.is_debug
|
||||
disp('Initial parameters:');
|
||||
disp(params);
|
||||
end
|
||||
|
||||
obj.params = params;
|
||||
end
|
||||
|
||||
function [fitResult, gof] = fit(obj, data, x, y, varargin)
|
||||
% Perform 2D nonlinear least squares fit for BEC + thermal cloud.
|
||||
% Optionally includes rotation of the cloud profile.
|
||||
|
||||
data = double(data);
|
||||
figure
|
||||
imagesc(data);
|
||||
axis equal tight;
|
||||
colorbar;
|
||||
colormap('jet');
|
||||
|
||||
% Perform fitting
|
||||
if isempty(obj.params)
|
||||
obj.guess(data, x, y, varargin{:});
|
||||
end
|
||||
|
||||
% Prepare grid and data
|
||||
|
||||
% Prepare fitting data
|
||||
[X, Y] = meshgrid(x, y);
|
||||
xyData = [X(:), Y(:)];
|
||||
zData = double(data(:));
|
||||
|
||||
% Create fit options and fittype
|
||||
|
||||
% Create fit options
|
||||
options = fitoptions('Method', 'NonlinearLeastSquares');
|
||||
|
||||
|
||||
if obj.params.([obj.prefix 'rot_angle']).vary
|
||||
% Include rotation parameter
|
||||
% Define parameter order
|
||||
paramOrder = {[obj.prefix 'amp_bec'], [obj.prefix 'amp_th'], ...
|
||||
[obj.prefix 'x0_bec'], [obj.prefix 'y0_bec'], ...
|
||||
[obj.prefix 'x0_th'], [obj.prefix 'y0_th'], ...
|
||||
[obj.prefix 'sigmax_bec'], [obj.prefix 'sigmay_bec'], ...
|
||||
[obj.prefix 'sigma_th'], [obj.prefix 'rot_angle']};
|
||||
|
||||
% Start point, lower and upper bounds
|
||||
|
||||
% Create StartPoint, Lower, and Upper vectors
|
||||
startPoint = zeros(1, length(paramOrder));
|
||||
lowerBounds = zeros(1, length(paramOrder));
|
||||
upperBounds = zeros(1, length(paramOrder));
|
||||
|
||||
|
||||
for i = 1:length(paramOrder)
|
||||
paramName = paramOrder{i};
|
||||
startPoint(i) = obj.params.(paramName).value;
|
||||
lowerBounds(i) = obj.params.(paramName).min;
|
||||
upperBounds(i) = obj.params.(paramName).max;
|
||||
end
|
||||
|
||||
|
||||
% Set fitting options
|
||||
options.StartPoint = startPoint;
|
||||
options.Lower = lowerBounds;
|
||||
options.Upper = upperBounds;
|
||||
|
||||
|
||||
% Define fit type
|
||||
ft = fittype(@(amp_bec, amp_th, x0_bec, y0_bec, x0_th, y0_th, ...
|
||||
sigmax_bec, sigmay_bec, sigma_th, rot_angle, x, y) ...
|
||||
obj.density_profile_BEC_2d(x, y, amp_bec, amp_th, x0_bec, y0_bec, ...
|
||||
x0_th, y0_th, sigmax_bec, sigmay_bec, sigma_th, rot_angle), ...
|
||||
'independent', {'x', 'y'}, 'dependent', 'z');
|
||||
else
|
||||
% No rotation
|
||||
% Define parameter order
|
||||
paramOrder = {[obj.prefix 'amp_bec'], [obj.prefix 'amp_th'], ...
|
||||
[obj.prefix 'x0_bec'], [obj.prefix 'y0_bec'], ...
|
||||
[obj.prefix 'x0_th'], [obj.prefix 'y0_th'], ...
|
||||
[obj.prefix 'sigmax_bec'], [obj.prefix 'sigmay_bec'], ...
|
||||
[obj.prefix 'sigma_th']};
|
||||
|
||||
|
||||
% Create StartPoint, Lower, and Upper vectors
|
||||
startPoint = zeros(1, length(paramOrder));
|
||||
lowerBounds = zeros(1, length(paramOrder));
|
||||
upperBounds = zeros(1, length(paramOrder));
|
||||
|
||||
|
||||
for i = 1:length(paramOrder)
|
||||
paramName = paramOrder{i};
|
||||
startPoint(i) = obj.params.(paramName).value;
|
||||
lowerBounds(i) = obj.params.(paramName).min;
|
||||
upperBounds(i) = obj.params.(paramName).max;
|
||||
end
|
||||
|
||||
|
||||
% Set fitting options
|
||||
options.StartPoint = startPoint;
|
||||
options.Lower = lowerBounds;
|
||||
options.Upper = upperBounds;
|
||||
|
||||
|
||||
% Define fit type
|
||||
ft = fittype(@(amp_bec, amp_th, x0_bec, y0_bec, x0_th, y0_th, ...
|
||||
sigmax_bec, sigmay_bec, sigma_th, x, y) ...
|
||||
obj.density_profile_BEC_2d(x, y, amp_bec, amp_th, x0_bec, y0_bec, ...
|
||||
x0_th, y0_th, sigmax_bec, sigmay_bec, sigma_th, 0), ...
|
||||
'independent', {'x', 'y'}, 'dependent', 'z');
|
||||
end
|
||||
|
||||
|
||||
% Perform fitting
|
||||
[obj.fitResult, obj.gof] = fit(xyData, zData, ft, options);
|
||||
fitResult = obj.fitResult;
|
||||
gof = obj.gof;
|
||||
|
||||
% Compute condensate fraction
|
||||
|
||||
% Post-processing check
|
||||
if obj.post_check
|
||||
bval = coeffvalues(obj.fitResult);
|
||||
paramNames = coeffnames(obj.fitResult);
|
||||
|
||||
% Extract parameter values
|
||||
for i = 1:length(paramNames)
|
||||
eval([paramNames{i} ' = bval(i);']);
|
||||
end
|
||||
|
||||
% Calculate number of atoms around the BEC
|
||||
tf_fit = obj.ThomasFermi_2d(xyData(:,1), xyData(:,2), x0_bec, y0_bec, amp_bec, sigmax_bec, sigmay_bec);
|
||||
tf_fit_2 = obj.ThomasFermi_2d(xyData(:,1), xyData(:,2), x0_bec, y0_bec, amp_bec, 1.5*sigmax_bec, 1.5*sigmay_bec);
|
||||
|
||||
mask = tf_fit > 0;
|
||||
mask_2 = tf_fit_2 > 0;
|
||||
|
||||
N_c = sum(zData(mask & ~mask_2));
|
||||
N_a = obj.atom_n_conv * N_c;
|
||||
|
||||
% If too few atoms are found around the BEC, refit (BEC only)
|
||||
if N_a < 6615
|
||||
if obj.is_debug
|
||||
disp('No thermal cloud detected, performing BEC-only fit');
|
||||
end
|
||||
|
||||
% Update parameters
|
||||
obj.params.([obj.prefix 'amp_th']).value = 0;
|
||||
obj.params.([obj.prefix 'amp_th']).vary = false;
|
||||
|
||||
obj.params.([obj.prefix 'x0_th']).value = 1;
|
||||
obj.params.([obj.prefix 'x0_th']).vary = false;
|
||||
|
||||
obj.params.([obj.prefix 'y0_th']).value = 1;
|
||||
obj.params.([obj.prefix 'y0_th']).vary = false;
|
||||
|
||||
obj.params.([obj.prefix 'sigma_th']).value = 1;
|
||||
obj.params.([obj.prefix 'sigma_th']).vary = false;
|
||||
|
||||
% Refit
|
||||
[obj.fitResult, obj.gof] = fit(xyData, zData, ft, options);
|
||||
fitResult = obj.fitResult;
|
||||
gof = obj.gof;
|
||||
end
|
||||
end
|
||||
|
||||
% Calculate condensate fraction
|
||||
obj.cond_frac = obj.cal_cond_frac(X, Y);
|
||||
end
|
||||
|
||||
function thresh = calc_thresh(obj, data, thresh_val, sigma)
|
||||
% Binarize image for BEC detection
|
||||
if nargin < 3, thresh_val = 0.3; end
|
||||
if nargin < 4, sigma = 0.4; end
|
||||
|
||||
% Binarize image
|
||||
if nargin < 3
|
||||
thresh_val = 0.3;
|
||||
end
|
||||
if nargin < 4
|
||||
sigma = 0.4;
|
||||
end
|
||||
|
||||
blurred = imgaussfilt(data, sigma);
|
||||
thresh = blurred < max(blurred(:)) * thresh_val;
|
||||
thresh = double(~thresh);
|
||||
thresh = double(~thresh); % Invert and convert to double precision
|
||||
end
|
||||
|
||||
function center_pix = calc_cen_pix(obj, thresh)
|
||||
% Compute center of binarized image
|
||||
% Calculate the center of the binarized image
|
||||
[X, Y] = size(thresh);
|
||||
|
||||
thresh = thresh / sum(thresh(:));
|
||||
|
||||
% Edge distributions
|
||||
dx = sum(thresh, 2);
|
||||
dy = sum(thresh, 1);
|
||||
|
||||
% Expectation values
|
||||
center_pix = [sum(dx .* (1:X)'), sum(dy .* (1:Y))];
|
||||
end
|
||||
|
||||
function center = center_pix_conv(obj, center_pix, x, y)
|
||||
% Convert pixel indices to coordinate values
|
||||
% Convert pixel center to coordinate center
|
||||
center = [x(round(center_pix(1))), y(round(center_pix(2)))];
|
||||
end
|
||||
|
||||
function BEC_width_guess = guess_BEC_width(obj, thresh, center)
|
||||
% Estimate BEC width along x and y
|
||||
% Guess BEC width
|
||||
[X, Y] = size(thresh);
|
||||
|
||||
BEC_width_guess = [sum(thresh(:, round(center(2)))), sum(thresh(round(center(1)), :))];
|
||||
BEC_width_guess(BEC_width_guess<=0) = 1;
|
||||
|
||||
for i = 1:2
|
||||
if BEC_width_guess(i) <= 0
|
||||
BEC_width_guess(i) = 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function cond_frac = cal_cond_frac(obj, X, Y)
|
||||
% Compute condensate fraction from fitted BEC + thermal cloud profile
|
||||
% Calculate condensate fraction
|
||||
bval = coeffvalues(obj.fitResult);
|
||||
paramNames = coeffnames(obj.fitResult);
|
||||
|
||||
% Extract fit parameters
|
||||
% Extract parameter values
|
||||
for i = 1:length(paramNames)
|
||||
eval([paramNames{i} ' = bval(i);']);
|
||||
end
|
||||
|
||||
|
||||
if ~obj.params.([obj.prefix 'rot_angle']).vary
|
||||
rot_angle = 0;
|
||||
end
|
||||
|
||||
% Thomas-Fermi fit for condensate
|
||||
tf_fit = obj.ThomasFermi_2d(X, Y, x0_bec, y0_bec, amp_bec, sigmax_bec, sigmay_bec);
|
||||
% Total density profile (BEC + thermal cloud)
|
||||
fit_total = obj.density_profile_BEC_2d(X, Y, amp_bec, amp_th, x0_bec, y0_bec, ...
|
||||
x0_th, y0_th, sigmax_bec, sigmay_bec, sigma_th, rot_angle);
|
||||
fit_total = obj.density_profile_BEC_2d(X, Y, amp_bec, amp_th, x0_bec, y0_bec, x0_th, y0_th, sigmax_bec, sigmay_bec, sigma_th, rot_angle);
|
||||
|
||||
N_bec = sum(tf_fit(:));
|
||||
N_ges = sum(fit_total(:));
|
||||
@ -371,7 +605,7 @@ classdef DensityProfileBEC2DModel < handle
|
||||
end
|
||||
|
||||
function atom_n = return_atom_number(obj, X, Y, is_print)
|
||||
% Compute total number of atoms, BEC atoms, thermal atoms, and condensate fraction
|
||||
% Calculate atom number
|
||||
if nargin < 4
|
||||
is_print = true;
|
||||
end
|
||||
@ -379,7 +613,7 @@ classdef DensityProfileBEC2DModel < handle
|
||||
bval = coeffvalues(obj.fitResult);
|
||||
paramNames = coeffnames(obj.fitResult);
|
||||
|
||||
% Extract fit parameters
|
||||
% Extract parameter values
|
||||
for i = 1:length(paramNames)
|
||||
eval([paramNames{i} ' = bval(i);']);
|
||||
end
|
||||
@ -404,7 +638,7 @@ classdef DensityProfileBEC2DModel < handle
|
||||
end
|
||||
|
||||
function T = return_temperature(obj, tof, omg, is_print, eff_pix)
|
||||
% Compute temperature from thermal cloud width and time-of-flight
|
||||
% Calculate temperature
|
||||
if nargin < 3
|
||||
omg = [];
|
||||
end
|
||||
@ -418,42 +652,43 @@ classdef DensityProfileBEC2DModel < handle
|
||||
bval = coeffvalues(obj.fitResult);
|
||||
paramNames = coeffnames(obj.fitResult);
|
||||
|
||||
% Extract fit parameters
|
||||
% Extract parameter values
|
||||
for i = 1:length(paramNames)
|
||||
eval([paramNames{i} ' = bval(i);']);
|
||||
end
|
||||
|
||||
R_th = sigma_th * eff_pix * sqrt(2); % Thermal cloud radius
|
||||
R_th = sigma_th * eff_pix * sqrt(2);
|
||||
|
||||
% Physical constants
|
||||
u = 1.66053906660e-27; % Atomic mass unit [kg]
|
||||
k = 1.380649e-23; % Boltzmann constant [J/K]
|
||||
u = 1.66053906660e-27; % Atomic mass unit
|
||||
k = 1.380649e-23; % Boltzmann constant
|
||||
|
||||
if isempty(omg)
|
||||
% Free expansion
|
||||
T = R_th^2 * 164 * u / k / tof^2;
|
||||
else
|
||||
% Trap expansion included
|
||||
T = R_th^2 * 164 * u / k / (1/omg^2 + tof^2);
|
||||
end
|
||||
|
||||
if is_print
|
||||
fprintf('Temperature: %.2f nK\n', T * 1e9);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
methods (Static)
|
||||
|
||||
function res = ThomasFermi_2d(x, y, centerx, centery, amplitude, sigmax, sigmay)
|
||||
% 2D Thomas-Fermi distribution for a BEC (parabolic density profile)
|
||||
res = (1 - ((x - centerx)/sigmax).^2 - ((y - centery)/sigmay).^2);
|
||||
% Thomas-Fermi distribution function
|
||||
% tiny = 1e-15;
|
||||
res = (1 - ((x - centerx) / sigmax).^2 - ((y - centery) / sigmay).^2);
|
||||
res(res < 0) = 0;
|
||||
res = amplitude * res.^(3/2); % Standard TF 3/2 exponent
|
||||
res = amplitude * res.^(3/2);
|
||||
% res = amplitude * 5/(2*pi) / max(tiny, sigmax * sigmay) .* (res > 0) .* res;
|
||||
end
|
||||
|
||||
function res = polylog(power, numerator)
|
||||
% Approximate polylogarithm function using truncated series
|
||||
order = 20; % Truncation order
|
||||
% Polylogarithm function approximation
|
||||
order = 20;
|
||||
dataShape = size(numerator);
|
||||
numerator = repmat(numerator(:), 1, order);
|
||||
numerator = numerator .^ repmat(1:order, prod(dataShape), 1);
|
||||
@ -466,31 +701,31 @@ classdef DensityProfileBEC2DModel < handle
|
||||
end
|
||||
|
||||
function res = polylog2_2d(x, y, centerx, centery, amplitude, sigmax, sigmay)
|
||||
% 2D thermal cloud distribution using polylog(2)
|
||||
arg = exp(-((x - centerx).^2 / (2*sigmax^2)) - ((y - centery).^2 / (2*sigmay^2)));
|
||||
res = amplitude / 1.643 * FitModels.DensityProfileBEC2DModel.polylog(2, arg);
|
||||
% 2D polylogarithm function
|
||||
% tiny = 1e-15;
|
||||
arg = exp(-((x - centerx).^2 / (2 * sigmax^2)) - ((y - centery).^2 / (2 * sigmay^2)));
|
||||
res = amplitude / (1.643) .* FitModels.DensityProfileBEC2DModel.polylog(2, arg);
|
||||
end
|
||||
|
||||
function res = density_profile_BEC_2d(x, y, amp_bec, amp_th, x0_bec, y0_bec, ...
|
||||
x0_th, y0_th, sigmax_bec, sigmay_bec, sigma_th, rot_angle)
|
||||
% Combined 2D density profile: BEC (Thomas-Fermi) + thermal cloud (polylog)
|
||||
|
||||
function res = density_profile_BEC_2d(x, y, amp_bec, amp_th, x0_bec, y0_bec, x0_th, y0_th, sigmax_bec, sigmay_bec, sigma_th, rot_angle)
|
||||
% BEC density profile function
|
||||
if nargin < 12
|
||||
rot_angle = 0;
|
||||
end
|
||||
|
||||
% Rotate coordinates if needed
|
||||
% Rotate coordinates (if needed)
|
||||
if rot_angle ~= 0
|
||||
rot_angle_rad = -rot_angle * pi/180; % Clockwise rotation
|
||||
rot_angle_rad = -rot_angle * pi/180; % Negative sign means clockwise rotation
|
||||
|
||||
% Rotate coordinates
|
||||
x_rot = x * cos(rot_angle_rad) + y * sin(rot_angle_rad);
|
||||
y_rot = -x * sin(rot_angle_rad) + y * cos(rot_angle_rad);
|
||||
|
||||
% Rotate centers
|
||||
% Rotate BEC center
|
||||
x0_bec_rot = x0_bec * cos(rot_angle_rad) + y0_bec * sin(rot_angle_rad);
|
||||
y0_bec_rot = -x0_bec * sin(rot_angle_rad) + y0_bec * cos(rot_angle_rad);
|
||||
|
||||
% Rotate thermal center
|
||||
x0_th_rot = x0_th * cos(rot_angle_rad) + y0_th * sin(rot_angle_rad);
|
||||
y0_th_rot = -x0_th * sin(rot_angle_rad) + y0_th * cos(rot_angle_rad);
|
||||
|
||||
@ -502,20 +737,20 @@ classdef DensityProfileBEC2DModel < handle
|
||||
y0_th = y0_th_rot;
|
||||
end
|
||||
|
||||
% Thomas-Fermi part (BEC)
|
||||
% Calculate Thomas-Fermi part
|
||||
TF_part = FitModels.DensityProfileBEC2DModel.ThomasFermi_2d(x, y, x0_bec, y0_bec, amp_bec, sigmax_bec, sigmay_bec);
|
||||
|
||||
% Polylog thermal part
|
||||
% Calculate polylogarithm part
|
||||
poly_part = FitModels.DensityProfileBEC2DModel.polylog2_2d(x, y, x0_th, y0_th, amp_th, sigma_th, sigma_th);
|
||||
|
||||
% Total density
|
||||
% Total sum
|
||||
res = TF_part + poly_part;
|
||||
end
|
||||
|
||||
function res = density_1d(x, x0_bec, x0_th, amp_bec, amp_th, sigma_bec, sigma_th)
|
||||
% 1D density profile (Thomas-Fermi + thermal polylog)
|
||||
thermal_part = amp_th / 1.643 * polylog_int(exp(-0.5 * (x - x0_th).^2 / sigma_th^2));
|
||||
TF_part = amp_bec * (1 - ((x - x0_bec)/sigma_bec).^2);
|
||||
TF_part = amp_bec * (1 - ((x - x0_bec) / sigma_bec).^2);
|
||||
TF_part(TF_part < 0) = 0;
|
||||
TF_part = TF_part.^(3/2);
|
||||
res = thermal_part + TF_part;
|
||||
@ -524,8 +759,10 @@ classdef DensityProfileBEC2DModel < handle
|
||||
|
||||
end
|
||||
|
||||
% Helper function: polylogarithm interpolation
|
||||
|
||||
function res = polylog_int(x)
|
||||
|
||||
% Create interpolation table (simplified version)
|
||||
x_int = linspace(0, 1.00001, 1000);
|
||||
poly_tab = zeros(size(x_int));
|
||||
|
||||
@ -533,21 +770,32 @@ function res = polylog_int(x)
|
||||
poly_tab(i) = sum((x_int(i).^(1:20)) ./ (1:20).^2);
|
||||
end
|
||||
|
||||
% Linear interpolation
|
||||
res = interp1(x_int, poly_tab, x, 'linear', 'extrap');
|
||||
|
||||
end
|
||||
|
||||
function [fitResult, gof] = fit_1d_bimodal(x, y, initialParams)
|
||||
% 1D bimodal fitting function
|
||||
% Input:
|
||||
% x - independent variable data
|
||||
% y - dependent variable data
|
||||
% initialParams - structure of initial parameters
|
||||
% Output:
|
||||
% fitResult - fitting result
|
||||
% gof - goodness-of-fit statistics
|
||||
|
||||
% Define 1D bimodal fitting function
|
||||
bimodal1d = @(amp_bec, amp_th, x0_bec, x0_th, sigma_bec, sigma_th, x) ...
|
||||
FitModels.DensityProfileBEC2DModel.density_1d(x, x0_bec, x0_th, amp_bec, amp_th, sigma_bec, sigma_th);
|
||||
paramNames = {'amp_bec', 'amp_th', 'x0_bec', 'x0_th', 'sigma_bec', 'sigma_th'};
|
||||
|
||||
% Create fit type
|
||||
ft = fittype(bimodal1d, 'independent', 'x', 'dependent', 'y');
|
||||
|
||||
% Set fit options
|
||||
options = fitoptions(ft);
|
||||
|
||||
% paramNames = fieldnames(initialParams);
|
||||
% Set initial parameters and bounds
|
||||
startPoint = zeros(1, length(paramNames));
|
||||
lowerBounds = zeros(1, length(paramNames));
|
||||
upperBounds = zeros(1, length(paramNames));
|
||||
@ -563,6 +811,6 @@ function [fitResult, gof] = fit_1d_bimodal(x, y, initialParams)
|
||||
options.Lower = lowerBounds;
|
||||
options.Upper = upperBounds;
|
||||
|
||||
% Perform fitting
|
||||
[fitResult, gof] = fit(x(:), y(:), ft, options);
|
||||
|
||||
end
|
@ -199,4 +199,5 @@ classdef TwoGaussian2DModel < handle
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -0,0 +1,117 @@
|
||||
%% ===== BEC-Droplets Settings =====
|
||||
|
||||
% Specify data location to run analysis on
|
||||
dataSources = {
|
||||
struct('sequence', 'Evaporative_Cooling', ...
|
||||
'date', '2025/08/13', ...
|
||||
'runs', [30]) % specify run numbers as a string in "" or just as a numeric value
|
||||
};
|
||||
|
||||
options = struct();
|
||||
|
||||
% File paths
|
||||
options.baseDataFolder = '//DyLabNAS/Data';
|
||||
options.FullODImagesFolder = 'E:/Data - Experiment/FullODImages/202508';
|
||||
options.measurementName = 'BECToDroplets';
|
||||
scriptFullPath = mfilename('fullpath');
|
||||
options.saveDirectory = fileparts(scriptFullPath);
|
||||
|
||||
% Camera / imaging settings
|
||||
options.cam = 3; % 1 - ODT_1_Axis_Camera; 2 - ODT_2_Axis_Camera; 3 - Horizontal_Axis_Camera;, 4 - Vertical_Axis_Camera;
|
||||
options.angle = 0; % angle by which image will be rotated
|
||||
options.center = [840, 972];
|
||||
options.span = [100, 100];
|
||||
options.fraction = [0.1, 0.1];
|
||||
options.pixel_size = 5.86e-6; % in meters
|
||||
options.magnification = 2.2218;
|
||||
options.ImagingMode = 'LowIntensity';
|
||||
options.PulseDuration = 25e-6; % in s
|
||||
|
||||
% Fourier analysis settings
|
||||
options.theta_min = deg2rad(0);
|
||||
options.theta_max = deg2rad(180);
|
||||
options.N_radial_bins = 500;
|
||||
options.Radial_Sigma = 2;
|
||||
options.Radial_WindowSize = 5; % odd number
|
||||
|
||||
options.k_min = 1.2771; % μm⁻¹
|
||||
options.k_max = 2.5541; % μm⁻¹
|
||||
options.N_angular_bins = 180;
|
||||
options.Angular_Threshold = 75;
|
||||
options.Angular_Sigma = 2;
|
||||
options.Angular_WindowSize = 5;
|
||||
options.zoom_size = 50;
|
||||
|
||||
% Flags
|
||||
options.skipUnshuffling = false;
|
||||
options.skipNormalization = false;
|
||||
|
||||
options.skipFringeRemoval = true;
|
||||
options.skipPreprocessing = true;
|
||||
options.skipMasking = true;
|
||||
options.skipIntensityThresholding = true;
|
||||
options.skipBinarization = true;
|
||||
|
||||
options.skipFullODImagesFolderUse = true;
|
||||
options.skipSaveData = false;
|
||||
options.skipSaveFigures = true;
|
||||
options.skipSaveProcessedOD = true;
|
||||
options.skipLivePlot = true;
|
||||
options.showProgressBar = true;
|
||||
|
||||
% Extras
|
||||
options.font = 'Bahnschrift';
|
||||
switch options.measurementName
|
||||
case 'BECToDroplets'
|
||||
options.scan_parameter = 'z_offset';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Droplets';
|
||||
case 'BECToStripes'
|
||||
options.scan_parameter = 'rot_mag_field';
|
||||
options.flipSortOrder = true;
|
||||
options.scanParameterUnits = 'gauss';
|
||||
options.titleString = 'BEC to Stripes';
|
||||
case 'DropletsToStripes'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Droplets to Stripes';
|
||||
case 'StripesToDroplets'
|
||||
options.scan_parameter = 'ps_rot_mag_fin_pol_angle';
|
||||
options.flipSortOrder = false;
|
||||
options.scanParameterUnits = 'degrees';
|
||||
options.titleString = 'Stripes to Droplets';
|
||||
end
|
||||
|
||||
%% ===== Collect Images and Launch Viewer =====
|
||||
|
||||
[options.selectedPath, options.folderPath] = Helper.selectDataSourcePath(dataSources, options);
|
||||
|
||||
[od_imgs, scan_parameter_values, scan_reference_values, file_list] = Helper.collectODImages(options);
|
||||
|
||||
%% Fit model and extract quantities
|
||||
|
||||
% --- Specify model and quantities to extract ---
|
||||
model = FitModels.DensityProfileBEC2DModel();
|
||||
quantities = {'atom_number','condensate_fraction','temperature'};
|
||||
|
||||
% --- Optional plotting configuration ---
|
||||
plotConfig = struct();
|
||||
plotConfig.fontName = 'Bahnschrift';
|
||||
plotConfig.fontSize = 16;
|
||||
plotConfig.fontWeight = 'bold';
|
||||
plotConfig.colormapName = 'sky';
|
||||
plotConfig.scatterLineSpec = '-o';
|
||||
plotConfig.figureTag = 'BatchBECViewer';
|
||||
plotConfig.bottomRowTitles = {'Condensed Atom Number','Condensate Fraction','Temperature'};
|
||||
plotConfig.bottomRowLabels = {'# (\times 10^4)','# (%)','# (nK)'};
|
||||
plotConfig.bottomRowUnits = [1e-4,1e2,1e9];
|
||||
|
||||
% --- Extra parameters ---
|
||||
extraParams = struct();
|
||||
extraParams.ToF = 20e-3;
|
||||
|
||||
% --- Run viewer ---
|
||||
results = Analyzer.runFitProgressViewer(od_imgs, model, quantities, plotConfig, extraParams);
|
||||
|
Loading…
Reference in New Issue
Block a user