diff --git a/Data-Analyzer/+Analyzer/conductSpectralAnalysis.m b/Data-Analyzer/+Analyzer/conductSpectralAnalysis.m index 6c55b1d..c320885 100644 --- a/Data-Analyzer/+Analyzer/conductSpectralAnalysis.m +++ b/Data-Analyzer/+Analyzer/conductSpectralAnalysis.m @@ -2,23 +2,20 @@ function results = conductSpectralAnalysis(od_imgs, scan_parameter_values, optio %% Performs Fourier analysis on a set of optical density (OD) images. % Computes radial and angular spectral distributions, optionally plots -% results, saves figures, and can render a video of the analysis. +% results, and saves figures. % % Inputs: % od_imgs - cell array of OD images % scan_parameter_values - array of scan parameter values corresponding to each image % OPTIONS - -% saveDirectory - directory to save files -% savefileName - base filename for saved figures/video -% skipMovieRender - skip creating the video of analysis +% saveDirectory - base directory to save results % skipSaveFigures - skip saving plots -% skipSaveOD - skip saving OD images as .mat % skipPreprocessing - skip preprocessing of images before FFT % skipMasking - skip masking of OD images % skipIntensityThresholding- skip thresholding of intensity % skipBinarization - skip binarization of OD images % skipNormalization - skip normalization when plotting angular spectrum -% skipLivePlot = skip live plotting of figures +% skipLivePlot - skip live plotting of figures % pixel_size - physical pixel size of camera sensor (m) % magnification - imaging magnification % zoom_size - number of pixels to crop around FFT center @@ -31,8 +28,13 @@ function results = conductSpectralAnalysis(od_imgs, scan_parameter_values, optio % Radial_WindowSize - window size for smoothing radial spectrum % scan_parameter - string, type of scan parameter (used in plot text) % font - font name for plots +% +% Outputs: +% results - struct containing spectra and analysis results +% Figures (if enabled) are saved into: +% [saveDirectory]/Results/SpectralAnalysisSavedFigures/ - %% Unpack struct arguments + %% ===== Unpack struct arguments ===== pixel_size = options.pixel_size; magnification = options.magnification; zoom_size = options.zoom_size; @@ -51,10 +53,7 @@ function results = conductSpectralAnalysis(od_imgs, scan_parameter_values, optio skipIntensityThresholding = options.skipIntensityThresholding; skipBinarization = options.skipBinarization; skipLivePlot = options.skipLivePlot; - skipMovieRender = options.skipMovieRender; skipSaveFigures = options.skipSaveFigures; - skipSaveOD = options.skipSaveOD; - savefileName = options.savefileName; saveDirectory = options.saveDirectory; scan_parameter = options.scan_parameter; font = options.font; @@ -70,33 +69,16 @@ function results = conductSpectralAnalysis(od_imgs, scan_parameter_values, optio S_k_all = cell(1, N_shots); S_k_smoothed_all = cell(1, N_shots); S_theta_norm_all = cell(1, N_shots); + PS_all = cell(1, N_shots); % 2D FFT power spectrum |F(kx,ky)|^2 - - % Optional save directory override - if ~isempty(saveDirectory) - savefileName = fullfile(saveDirectory, savefileName); - end - - % Prepare video if enabled - if ~skipMovieRender - videoFile = VideoWriter([savefileName '.mp4'], 'MPEG-4'); - videoFile.Quality = 100; - videoFile.FrameRate = 2; - open(videoFile); - end - % Prepare folder to save figures if ~skipSaveFigures - saveFolder = [savefileName '_SavedFigures']; + saveFolder = fullfile(saveDirectory, "Results", "SpectralAnalysisSavedFigures"); if ~exist(saveFolder, 'dir') mkdir(saveFolder); end end - % Initialize lists for power spectra and radial spectra - PS_all = cell(1, N_shots); % 2D FFT power spectrum |F(kx,ky)|^2 - - %% ===== Main loop over images ===== for k = 1:N_shots IMG = od_imgs{k}; @@ -253,29 +235,16 @@ function results = conductSpectralAnalysis(od_imgs, scan_parameter_values, optio ax.YMinorGrid = 'on'; end - %% ===== Save outputs ===== - if ~skipMovieRender - frame = getframe(gcf); - writeVideo(videoFile, frame); - end + %% ===== Save figures ===== if ~skipSaveFigures fileNamePNG = fullfile(saveFolder, sprintf('fft_analysis_img_%03d.png', k)); print(gcf, fileNamePNG, '-dpng', '-r100'); - end - if ~skipSaveOD - odDataStruct = struct(); - odDataStruct.IMG = IMG; - odDataStruct.x = x; - odDataStruct.y = y; - odDataStruct.scan_parameter_value = scan_parameter_values(k); - save(fullfile(saveFolder, sprintf('od_image_%03d.mat', k)), '-struct', 'odDataStruct'); - end - if skipMovieRender && skipSaveFigures + elseif ~skipLivePlot pause(0.5); end end - % Package results into struct + % Package results into struct results = struct(); results.kx = kx; results.ky = ky; @@ -290,7 +259,4 @@ function results = conductSpectralAnalysis(od_imgs, scan_parameter_values, optio results.S_theta_norm_all = S_theta_norm_all; results.angular_spectral_weight = angular_spectral_weight; - if ~skipMovieRender - close(videoFile); - end end diff --git a/Data-Analyzer/+Analyzer/performAnalysis.m b/Data-Analyzer/+Analyzer/performAnalysis.m index 05c012d..e526572 100644 --- a/Data-Analyzer/+Analyzer/performAnalysis.m +++ b/Data-Analyzer/+Analyzer/performAnalysis.m @@ -31,17 +31,14 @@ function results = performAnalysis(options) options.skipBinarization (1,1) logical options.skipNormalization (1,1) logical options.skipLivePlot (1,1) logical - options.skipMovieRender (1,1) logical options.skipSaveFigures (1,1) logical - options.skipSaveOD (1,1) logical options.showProgressBar (1,1) logical - options.savefileName (1,:) char + options.measurementName (1,:) char options.folderPath (1,:) char options.baseDataFolder (1,:) char options.saveDirectory (1,:) char options.titleString (1,:) char options.font (1,:) char - end % Collect OD images diff --git a/Data-Analyzer/+Analyzer/runInteractiveODImageViewer.m b/Data-Analyzer/+Analyzer/runInteractiveODImageViewer.m index 4b91093..bb0f375 100644 --- a/Data-Analyzer/+Analyzer/runInteractiveODImageViewer.m +++ b/Data-Analyzer/+Analyzer/runInteractiveODImageViewer.m @@ -68,7 +68,7 @@ function runInteractiveODImageViewer(od_imgs, scan_parameter_values, file_list, shortName = [fname, ext]; % Update figure title with shot + filename - if strcmp(options.scan_parameter, 'rot_mag_fin_pol_angle') + if strcmp(options.scan_parameter, 'ps_rot_mag_fin_pol_angle') hFig.Name = sprintf('Shot %d | %s', idx, shortName); txtHandle.String = sprintf('%.1f^\\circ', scan_parameter_values(idx)); else diff --git a/Data-Analyzer/+Helper/collectODImages.m b/Data-Analyzer/+Helper/collectODImages.m index 1393da5..a497a14 100644 --- a/Data-Analyzer/+Helper/collectODImages.m +++ b/Data-Analyzer/+Helper/collectODImages.m @@ -1,4 +1,4 @@ -function [ordered_od_imgs, ordered_scan_parameter_values, ordered_file_list] = collectODImages(options) +function [od_imgs, scan_parameter_values, file_list] = collectODImages(options) %% Applies cropping, background subtraction, and optional fringe removal, optional unshuffling on OD image dataset % Automatically reuses in-memory full dataset if available; % otherwise, reads and processes raw HDF5 data. @@ -17,17 +17,29 @@ function [ordered_od_imgs, ordered_scan_parameter_values, ordered_file_list] = c % .scan_reference_values: reference values for unshuffling % % Outputs: -% ordered_od_imgs : cell array of processed OD images (ordered) -% ordered_scan_parameter_values: vector of scan parameter values (ordered) -% ordered_file_list : cell array of file names (ordered) +% od_imgs : cell array of processed OD images +% scan_parameter_values: vector of scan parameter values +% file_list : cell array of file names - % --- Early exit if processed data already exist --- - if evalin('base', 'exist(''od_imgs'',''var'') && exist(''scan_parameter_values'',''var'') && exist(''file_list'',''var'')') - fprintf('\nReusing processed OD images, scan parameters, and file list from memory.\n'); - ordered_od_imgs = evalin('base','od_imgs'); - ordered_scan_parameter_values = evalin('base','scan_parameter_values'); - ordered_file_list = evalin('base','file_list'); - return; % ✅ skip rest of the function + % --- Early exit if processed data already exist AND options match --- + reuseVarsExist = evalin('base', ... + 'exist(''od_imgs'',''var'') && exist(''scan_parameter_values'',''var'') && exist(''file_list'',''var'') && exist(''prior_options'',''var'')'); + + if reuseVarsExist + prior_options = evalin('base','prior_options'); + + % Define which fields are critical for reuse + critical_fields = {'folderPath','cam','angle','ImagingMode','PulseDuration','center','span','fraction','removeFringes','skipUnshuffling','scan_reference_values'}; + + if ~haveOptionsChanged(options, prior_options, critical_fields) + fprintf('\nReusing processed OD images, scan parameters, and file list from memory (options unchanged).\n'); + od_imgs = evalin('base','od_imgs'); + scan_parameter_values = evalin('base','scan_parameter_values'); + file_list = evalin('base','file_list'); + return; % ✅ skip rest of the function + else + fprintf('\nProcessed-data-related options changed. Reprocessing full OD image dataset...\n'); + end end % --- Check if the full OD dataset and scan parameters exist in workspace --- @@ -37,12 +49,29 @@ function [ordered_od_imgs, ordered_scan_parameter_values, ordered_file_list] = c evalin('base', 'exist(''raw_file_list'', ''var'')'); if fullDataExists - % Both required datasets exist, use them directly - fprintf('\nReusing full OD image dataset and scan parameters from memory.\n'); - full_od_imgs = evalin('base', 'full_od_imgs'); - full_bkg_imgs = evalin('base', 'full_bkg_imgs'); - raw_scan_parameter_values = evalin('base', 'raw_scan_parameter_values'); - raw_file_list = evalin('base', 'raw_file_list'); + % Both required datasets exist, check if raw-data options changed + prior_options = evalin('base','prior_options'); + + % Define critical fields that affect raw-data computation + critical_raw_fields = {'folderPath','cam','angle','ImagingMode','PulseDuration'}; + + if ~haveOptionsChanged(options, prior_options, critical_raw_fields) + fprintf('\nReusing full OD image dataset and scan parameters from memory.\n'); + full_od_imgs = evalin('base', 'full_od_imgs'); + full_bkg_imgs = evalin('base', 'full_bkg_imgs'); + raw_scan_parameter_values = evalin('base', 'raw_scan_parameter_values'); + raw_file_list = evalin('base', 'raw_file_list'); + else + fprintf('\nRaw-data-related options changed. Recomputing full OD image dataset...\n'); + [full_od_imgs, full_bkg_imgs, raw_scan_parameter_values, raw_file_list] = Helper.processRawData(options); + + % Save raw full dataset for reuse + assignin('base', 'full_od_imgs', full_od_imgs); + assignin('base', 'full_bkg_imgs', full_bkg_imgs); + assignin('base', 'raw_scan_parameter_values', raw_scan_parameter_values); + assignin('base', 'raw_file_list', raw_file_list); + fprintf('\nCompleted recomputing OD images. Stored in workspace for reuse.\n'); + end else % Either dataset is missing, process raw HDF5 files completely fprintf('\nFull OD image dataset or scan parameters not found in memory.\n'); @@ -53,9 +82,9 @@ function [ordered_od_imgs, ordered_scan_parameter_values, ordered_file_list] = c assignin('base', 'full_bkg_imgs', full_bkg_imgs); assignin('base', 'raw_scan_parameter_values', raw_scan_parameter_values); assignin('base', 'raw_file_list', raw_file_list); - fprintf('\nCompleted computing OD images. Stored in workspace for reuse.\n'); + fprintf('\nCompleted computing OD images and will be stored in workspace for reuse.\n'); end - + nFiles = size(full_od_imgs, 3); % --- Preallocate arrays for processed images --- @@ -64,20 +93,20 @@ function [ordered_od_imgs, ordered_scan_parameter_values, ordered_file_list] = c % --- Process each image: crop and subtract background --- for k = 1:nFiles - od_img = full_od_imgs(:,:,k); % original full OD image, never modified - bkg_img = full_bkg_imgs(:,:,k); % original full background image, never modified - if any(isnan(od_img(:))) + full_od_img = full_od_imgs(:,:,k); % original full OD image, never modified + full_bkg_img = full_bkg_imgs(:,:,k); % original full background image, never modified + if any(isnan(full_od_img(:))) absimages(:,:,k) = nan(options.span(1)+1, options.span(2)+1, 'single'); continue end - if any(isnan(bkg_img(:))) + if any(isnan(full_bkg_img(:))) refimages(:,:,k) = nan(options.span(1)+1, options.span(2)+1, 'single'); continue end % Crop image around the region of interest - cropped_absimage = Helper.cropODImage(od_img, options.center, options.span); - cropped_refimage = Helper.cropODImage(bkg_img, options.center, options.span); + cropped_absimage = Helper.cropODImage(full_od_img, options.center, options.span); + cropped_refimage = Helper.cropODImage(full_bkg_img, options.center, options.span); % Subtract background offset based on fraction processed_absimage = Helper.subtractBackgroundOffset(cropped_absimage, options.fraction); @@ -93,10 +122,10 @@ function [ordered_od_imgs, ordered_scan_parameter_values, ordered_file_list] = c fprintf('\nApplying fringe removal to processed images...\n'); optrefimages = Helper.removeFringesInImage(absimages, refimages); absimages_fringe_removed = absimages - optrefimages; - processed_od_imgs = arrayfun(@(i) absimages_fringe_removed(:,:,i), 1:nFiles, 'UniformOutput', false); + od_imgs = arrayfun(@(i) absimages_fringe_removed(:,:,i), 1:nFiles, 'UniformOutput', false); fprintf('\nFringe removal completed.\n'); else - processed_od_imgs = arrayfun(@(i) absimages(:,:,i), 1:nFiles, 'UniformOutput', false); + od_imgs = arrayfun(@(i) absimages(:,:,i), 1:nFiles, 'UniformOutput', false); end % --- Optional unshuffling based on scan reference values --- @@ -108,12 +137,12 @@ function [ordered_od_imgs, ordered_scan_parameter_values, ordered_file_list] = c n_reps = n_total / n_values; ordered_scan_parameter_values = zeros(1, n_total); - ordered_od_imgs = cell(1, n_total); - ordered_file_list = cell(1, n_total); + ordered_od_imgs = cell(1, n_total); + ordered_file_list = cell(1, n_total); counter = 1; temp_scan_values = raw_scan_parameter_values; - temp_od_imgs = processed_od_imgs; + temp_od_imgs = od_imgs; temp_file_list = raw_file_list; for rep = 1:n_reps @@ -129,18 +158,51 @@ function [ordered_od_imgs, ordered_scan_parameter_values, ordered_file_list] = c counter = counter + 1; end end + od_imgs = ordered_od_imgs; + scan_parameter_values = ordered_scan_parameter_values; + file_list = ordered_file_list; fprintf('\nImage reordering completed.\n'); else % No unshuffling: keep original order - ordered_od_imgs = processed_od_imgs; - ordered_scan_parameter_values = raw_scan_parameter_values; - ordered_file_list = raw_file_list; + scan_parameter_values = raw_scan_parameter_values; + file_list = raw_file_list; end - % --- Save processed dataset for reuse --- - assignin('base', 'od_imgs', ordered_od_imgs); - assignin('base', 'scan_parameter_values', ordered_scan_parameter_values); - assignin('base', 'file_list', ordered_file_list); + % --- Save processed dataset and options for reuse --- + assignin('base', 'od_imgs', od_imgs); + assignin('base', 'scan_parameter_values', scan_parameter_values); + assignin('base', 'file_list', file_list); + assignin('base', 'prior_options', options); + + % --- Optionally save OD images as figures --- + if ~options.skipSaveFigures + odFolder = fullfile(saveDirectory, "Results", "ODImages"); + if ~exist(odFolder, 'dir') + mkdir(odFolder); + end + + for k = 1:length(od_imgs) + img = od_imgs{k}; + fileName = fullfile(odFolder, sprintf('OD_img_%03d.png', k)); + imwrite(mat2gray(img), fileName); + end + end fprintf('\nOD image dataset ready for further analysis.\n'); + +end + +% --- Local helper function to compare options --- +function changed = haveOptionsChanged(options, prior_options, critical_fields) + changed = false; + for f = critical_fields + fname = f{1}; + if isfield(options, fname) && isfield(prior_options, fname) + if ~isequal(options.(fname), prior_options.(fname)) + changed = true; return + end + elseif xor(isfield(options, fname), isfield(prior_options, fname)) + changed = true; return + end + end end \ No newline at end of file diff --git a/Data-Analyzer/+Plotter/plotAverageSpectra.m b/Data-Analyzer/+Plotter/plotAverageSpectra.m index 376ff8c..ba200ac 100644 --- a/Data-Analyzer/+Plotter/plotAverageSpectra.m +++ b/Data-Analyzer/+Plotter/plotAverageSpectra.m @@ -25,20 +25,12 @@ function plotAverageSpectra(scan_parameter_values, spectral_analysis_results, va addParameter(p, 'FigNum', 1, @(x) isnumeric(x) && isscalar(x)); addParameter(p, 'ColormapPS', Colormaps.coolwarm(), @(x) isnumeric(x) || ismatrix(x)); addParameter(p, 'Font', 'Arial', @ischar); - addParameter(p, 'SaveFileName', 'figure.fig', @ischar); + addParameter(p, 'SaveFileName', 'avgspectra.fig', @ischar); addParameter(p, 'SaveDirectory', pwd, @ischar); addParameter(p, 'SkipSaveFigures', false, @islogical); parse(p, varargin{:}); opts = p.Results; - scanParam = opts.ScanParameterName; - figNum = opts.FigNum; - colormapPS = opts.ColormapPS; - fontName = opts.Font; - saveFileName = opts.SaveFileName; - saveDirectory = opts.SaveDirectory; - skipSaveFigures = opts.SkipSaveFigures; - % --- Unique scan parameters --- [uniqueParams, ~, idx] = unique(scan_parameter_values); nParams = numel(uniqueParams); @@ -67,7 +59,7 @@ function plotAverageSpectra(scan_parameter_values, spectral_analysis_results, va avgS_theta = avgS_theta / nShots; % ==== Plot ==== - fig = figure(figNum); clf; + fig = figure(opts.FigNum); clf; set(fig, 'Color', 'w', 'Position', [400 200 1200 400]); tLayout = tiledlayout(1,3,'TileSpacing','compact','Padding','compact'); @@ -79,14 +71,14 @@ function plotAverageSpectra(scan_parameter_values, spectral_analysis_results, va imagesc(kx, ky, log(1 + avgPS)); axis image; set(gca, 'FontSize', axisFontSize, 'YDir', 'normal'); - xlabel('k_x [\mum^{-1}]','Interpreter','tex','FontSize',axisFontSize,'FontName',fontName); - ylabel('k_y [\mum^{-1}]','Interpreter','tex','FontSize',axisFontSize,'FontName',fontName); + xlabel('k_x [\mum^{-1}]','Interpreter','tex','FontSize',axisFontSize,'FontName',opts.Font); + ylabel('k_y [\mum^{-1}]','Interpreter','tex','FontSize',axisFontSize,'FontName',opts.Font); title('Average Power Spectrum','FontSize',titleFontSize,'FontWeight','bold'); - colormap(colormapPS); + colormap(opts.ColormapPS); colorbar; % --- Annotate scan parameter --- - if strcmp(scanParam,'ps_rot_mag_fin_pol_angle') + if strcmp(opts.ScanParameterName,'ps_rot_mag_fin_pol_angle') txt = sprintf('%.1f^\\circ', currentParam); else txt = sprintf('%.2f G', currentParam); @@ -119,8 +111,8 @@ function plotAverageSpectra(scan_parameter_values, spectral_analysis_results, va % --- Save figure --- saveFigure(fig, ... - 'SaveFileName', saveFileName, ... - 'SaveDirectory', saveDirectory, ... - 'SkipSaveFigures', skipSaveFigures); + 'SaveFileName', opts.SaveFileName, ... + 'SaveDirectory', opts.SaveDirectory, ... + 'SkipSaveFigures', opts.SkipSaveFigures); end end diff --git a/Data-Analyzer/+Scripts/BECToDroplets/plotImages.m b/Data-Analyzer/+Scripts/BECToDroplets/plotImages.m index d35c330..40c4108 100644 --- a/Data-Analyzer/+Scripts/BECToDroplets/plotImages.m +++ b/Data-Analyzer/+Scripts/BECToDroplets/plotImages.m @@ -10,10 +10,10 @@ dataSources = { options = struct(); % File / paths -options.baseDataFolder = '//DyLabNAS/Data'; -options.savefileName = 'BECToDroplets'; -scriptFullPath = mfilename('fullpath'); -options.saveDirectory = fileparts(scriptFullPath); +options.baseDataFolder = '//DyLabNAS/Data'; +options.measurementName = 'BECToDroplets'; +scriptFullPath = mfilename('fullpath'); +options.saveDirectory = fileparts(scriptFullPath); % Camera / imaging options.cam = 5; @@ -45,7 +45,7 @@ options.zoom_size = 50; % Scan parameter options.scan_parameter = 'rot_mag_field'; -switch options.savefileName +switch options.measurementName case 'BECToDroplets' options.scan_reference_values = [2.40, 2.39, 2.38, 2.37, 2.35, 2.34, 2.32, 2.30, 2.28, 2.26, 2.24, 2.22, 2.2, 2.15, 2.10, 2.05, 2, 1.95, 1.90, 1.85, 1.8]; options.titleString = 'BEC to Droplets'; diff --git a/Data-Analyzer/+Scripts/BECToDroplets/runFullAnalysis.m b/Data-Analyzer/+Scripts/BECToDroplets/runFullAnalysis.m index 53e0aee..3f04f86 100644 --- a/Data-Analyzer/+Scripts/BECToDroplets/runFullAnalysis.m +++ b/Data-Analyzer/+Scripts/BECToDroplets/runFullAnalysis.m @@ -10,10 +10,10 @@ dataSources = { options = struct(); % File / paths -options.baseDataFolder = '//DyLabNAS/Data'; -options.savefileName = 'BECToDroplets'; -scriptFullPath = mfilename('fullpath'); -options.saveDirectory = fileparts(scriptFullPath); +options.baseDataFolder = '//DyLabNAS/Data'; +options.measurementName = 'BECToDroplets'; +scriptFullPath = mfilename('fullpath'); +options.saveDirectory = fileparts(scriptFullPath); % Camera / imaging options.cam = 5; @@ -45,7 +45,7 @@ options.zoom_size = 50; % Scan parameter options.scan_parameter = 'rot_mag_field'; -switch options.savefileName +switch options.measurementName case 'BECToDroplets' options.scan_reference_values = [2.40, 2.39, 2.38, 2.37, 2.35, 2.34, 2.32, 2.30, 2.28, 2.26, 2.24, 2.22, 2.2, 2.15, 2.10, 2.05, 2, 1.95, 1.90, 1.85, 1.8]; options.titleString = 'BEC to Droplets'; diff --git a/Data-Analyzer/+Scripts/BECToDropletsToStripes/plotAnalysisResults.m b/Data-Analyzer/+Scripts/BECToDropletsToStripes/plotAnalysisResults.m new file mode 100644 index 0000000..3051592 --- /dev/null +++ b/Data-Analyzer/+Scripts/BECToDropletsToStripes/plotAnalysisResults.m @@ -0,0 +1,163 @@ +idx = 1; +compiled_results = results_all{idx}.results; +options.skipSaveFigures = false; + +%% ------------------ 1. Mean ± Std Plots ------------------ +% Plot Radial Spectral Contrast +Plotter.plotMeanWithSE(scan_parameter_values, compiled_results.spectral_analysis_results.radial_spectral_contrast, ... + 'Title', options.titleString, ... + 'XLabel', 'B (G)', ... + 'YLabel', 'Radial Spectral Contrast', ... + 'FigNum', 1, ... + 'FontName', options.font, ... + 'SaveFileName', 'RadialSpectralContrast.fig', ... + 'SaveDirectory', [options.saveDirectory '/Results'], ... + 'SkipSaveFigures', options.skipSaveFigures); + +% Plot Angular Spectral Weight +Plotter.plotMeanWithSE(scan_parameter_values, compiled_results.spectral_analysis_results.angular_spectral_weight, ... + 'Title', options.titleString, ... + 'XLabel', 'B (G)', ... + 'YLabel', 'Angular Spectral Weight', ... + 'FigNum', 2, ... + 'FontName', options.font, ... + 'SaveFileName', 'AngularSpectralWeight.fig', ... + 'SaveDirectory', [options.saveDirectory '/Results'], ... + 'SkipSaveFigures', options.skipSaveFigures); + +% Plot Peak Offset Angular Correlation +Plotter.plotMeanWithSE(options.scan_reference_values, compiled_results.custom_g_results.max_g2_all_per_scan_parameter_value, ... + 'Title', options.titleString, ... + 'XLabel', 'B (G)', ... + 'YLabel', '$\mathrm{max}[g^{(2)}_{[50,70]}(\delta\theta)]$', ... + 'FigNum', 3, ... + 'YLim', [0 1], ... + 'FontName', options.font, ... + 'SaveFileName', 'PeakOffsetAngularCorrelation.fig', ... + 'SaveDirectory', [options.saveDirectory '/Results'], ... + 'SkipSaveFigures', options.skipSaveFigures); + +%% ------------------ 2. g²(θ) across transition ------------------ +Plotter.plotG2(compiled_results.full_g2_results.g2_all, ... + compiled_results.full_g2_results.g2_error_all, ... + compiled_results.full_g2_results.theta_values, ... + options.scan_reference_values, ... + 'rot_mag_field', ... + 'Title', options.titleString, ... + 'XLabel', '$\delta\theta / \pi$', ... + 'YLabel', '$g^{(2)}(\delta\theta)$', ... + 'FigNum', 4, ... + 'FontName', options.font, ... + 'SkipSaveFigures', options.skipSaveFigures, ... + 'SaveFileName', 'G2ThetaAcrossTransition.fig', ... + 'SaveDirectory', [options.saveDirectory '/Results'], ... + 'Colormap', @Colormaps.coolwarm); + +%% ------------------ 3. PDF of max g² across transition ------------------ +Plotter.plotPDF(compiled_results.custom_g_results.max_g2_all_per_scan_parameter_value, options.scan_reference_values, ... + 'Title', options.titleString, ... + 'XLabel', 'B (G)', ... + 'YLabel', '$\mathrm{max}[g^{(2)}]$', ... + 'FigNum', 5, ... + 'FontName', options.font, ... + 'SkipSaveFigures', options.skipSaveFigures, ... + 'SaveFileName', 'PDF_MaxG2AcrossTransition.fig', ... + 'SaveDirectory', [options.saveDirectory '/Results'], ... + 'NumPoints', 200, ... + 'DataRange', [0 1.5], ... + 'Colormap', @Colormaps.coolwarm, ... + 'XLim', [min(options.scan_reference_values) max(options.scan_reference_values)]); + + +%% ------------------ 4. Cumulants across transition ------------------ +Plotter.plotCumulants(options.scan_reference_values, ... + {compiled_results.custom_g_results.mean_max_g2, compiled_results.custom_g_results.var_max_g2, compiled_results.custom_g_results.skew_max_g2_angle, compiled_results.custom_g_results.fourth_order_cumulant_max_g2}, ... + 'Title', 'Cumulants of Peak Offset Angular Correlation', ... + 'XLabel', 'B (G)', ... + 'FigNum', 6, ... + 'FontName', options.font, ... + 'MarkerSize', 6, ... + 'LineWidth', 1.5, ... + 'SkipSaveFigures', options.skipSaveFigures, ... + 'SaveFileName', 'CumulantOfPeakOffsetAngularCorrelation.fig', ... + 'SaveDirectory', [options.saveDirectory '/Results']); +%{ + +%% ------------------ 6. Average of Spectra Plots ------------------ + +Plotter.plotAverageSpectra(scan_parameter_values, ... + spectral_analysis_results, ... + 'ScanParameterName', scan_parameter, ... + 'FigNum', 7, ... + 'ColormapPS', Colormaps.coolwarm(), ... + 'Font', 'Bahnschrift', ... + 'SaveFileName', 'avgSpectra.fig', ... + 'SaveDirectory', [options.saveDirectory '/Results'], ... + 'SkipSaveFigures', options.skipSaveFigures); + +%% ------------------ 7. Compare quantities ------------------ +% Load Droplets → Stripes data +Data = load(dtsFile, ... + 'unique_scan_parameter_values', ... + 'mean_max_g2_values', ... + 'std_error_g2_values'); +dts_scan_parameter_values = Data.unique_scan_parameter_values; +dts_mean_mg2 = Data.mean_max_g2_values; +dts_stderr_mg2 = Data.std_error_g2_values; + +% Load Stripes → Droplets data +Data = load(stdFile, ... + 'unique_scan_parameter_values', ... + 'mean_max_g2_values', ... + 'std_error_g2_values'); +std_scan_parameter_values = Data.unique_scan_parameter_values; +std_mean_mg2 = Data.mean_max_g2_values; +std_stderr_mg2 = Data.std_error_g2_values; + +% Prepare cell arrays for multiple datasets +scanValsCell = {dts_scan_parameter_values, std_scan_parameter_values}; +meanValsCell = {dts_mean_mg2, std_mean_mg2}; +stderrValsCell = {dts_stderr_mg2, std_stderr_mg2}; + +% Compare datasets +compareMultipleDatasets(scanValsCell, meanValsCell, stderrValsCell, ... + 'FigNum', 8, ... + 'FontName', 'Bahnschrift', ... + 'MarkerSize', 6, ... + 'LineWidth', 1.5, ... + 'CapSize', 5, ... + 'YLim', [0 1], ... + 'Labels', {'Droplets → Stripes', 'Stripes → Droplets'}, ... + 'Title', 'AngularCorrelation_Comparison', ... + 'XLabel', 'B (G)', ... + 'YLabel', '$\mathrm{max}[g^{(2)}_{[50,70]}(\delta\theta)]$', ... + 'SkipSaveFigures', options.skipSaveFigures, ... + 'SaveDirectory', [options.saveDirectory '/Results'], ... + 'SaveFileName', 'AngularCorrelation_Comparison.fig'); + +%% ------------------ 8. Heatmaps ------------------ + +BFields = [2.35, 2.15, 2.0, 1.85, 1.7, 1.55, 1.4, 1.35]; + +% Heatmap of mean_max_g2_values +Plotter.plotHeatmap(compiled_results, options.scan_groups, BFields, 'mean_max_g2_values', ... + 'Colormap', @sky, ... + 'CLim', [0 1], ... + 'XLabel', '\alpha (degrees)', ... + 'YLabel', 'BField (G)', ... + 'Title', '$\mathrm{max}[g^{(2)}_{[50,70]}(\delta\theta)]$', ... + 'FigNum', 9, ... + 'SaveFileName', 'Heatmap_MaxG2.fig', ... + 'SaveDirectory', options.resultsDir); + +% Heatmap of radial_spectral_contrast +Plotter.plotHeatmap(compiled_results, options.scan_groups, BFields, 'radial_spectral_contrast', ... + 'Colormap', @sky, ... + 'CLim', [0 0.008], ... + 'XLabel', '\alpha (degrees)', ... + 'YLabel', 'BField (G)', ... + 'Title', 'Radial Spectral Contrast', ... + 'FigNum', 10, ... + 'SaveFileName', 'Heatmap_RadialSpectralContrast.fig', ... + 'SaveDirectory', options.resultsDir); +%} \ No newline at end of file diff --git a/Data-Analyzer/+Scripts/BECToDropletsToStripes/plotImages.m b/Data-Analyzer/+Scripts/BECToDropletsToStripes/plotImages.m new file mode 100644 index 0000000..60968bd --- /dev/null +++ b/Data-Analyzer/+Scripts/BECToDropletsToStripes/plotImages.m @@ -0,0 +1,116 @@ +%% ===== BEC-Droplets-Stripes Settings ===== + +% Specify data location to run analysis on +dataSources = { + struct('sequence', 'StructuralPhaseTransition', ... + 'date', '2025/08/16', ... + 'runs', [8]) % specify run numbers as a string in "" or just as a numeric value +}; + +options = struct(); + +% File / paths +options.baseDataFolder = '//DyLabNAS/Data'; +options.measurementName = 'DropletsToStripes'; +scriptFullPath = mfilename('fullpath'); +options.saveDirectory = fileparts(scriptFullPath); + +% Camera / imaging +options.cam = 5; +options.angle = 0; +options.center = [1420, 2050]; +options.span = [200, 200]; +options.fraction = [0.1, 0.1]; +options.pixel_size = 5.86e-6; % in meters +options.magnification = 24.6; +options.removeFringes = false; +options.ImagingMode = 'HighIntensity'; +options.PulseDuration = 5e-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; + +% Scan parameter +options.scan_parameter = 'ps_rot_mag_fin_pol_angle'; + +switch options.measurementName + case 'BECToDroplets' + options.scan_reference_values = [2.40, 2.39, 2.38, 2.37, 2.35, 2.34, 2.32, 2.30, 2.28, 2.26, 2.24, 2.22, 2.2, 2.15, 2.10, 2.05, 2, 1.95, 1.90, 1.85, 1.8]; + options.titleString = 'BEC to Droplets'; + case 'BECToStripes' + options.scan_reference_values = [2.45, 2.44, 2.43, 2.42, 2.4, 2.39, 2.38, 2.37, 2.36, 2.35, 2.34, 2.32, 2.3, 2.28, 2.25, 2.2, 2.15, 2.10, 2.0, 1.90, 1.8]; + options.titleString = 'BEC to Stripes'; + case 'DropletsToStripes' + options.scan_reference_values = [0, 5, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 35, 40]; + options.titleString = 'Droplets to Stripes'; + case 'StripesToDroplets' + options.scan_reference_values = fliplr([0, 5, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 35, 40]); + options.titleString = 'Stripes to Droplets'; +end + +% Flags +options.skipNormalization = false; +options.skipUnshuffling = false; +options.skipPreprocessing = true; +options.skipMasking = true; +options.skipIntensityThresholding = true; +options.skipBinarization = true; +options.skipSaveFigures = false; +options.skipLivePlot = false; +options.showProgressBar = true; + +% Extras +options.font = 'Bahnschrift'; + +%% ===== Build Paths from Data Sources ===== + +allPaths = {}; % initialize + +for i = 1:length(dataSources) + ds = dataSources{i}; + + % Split the date string into year/month/day + dateParts = strsplit(ds.date, '/'); + + for run = ds.runs + runStr = sprintf('%04d', run); % 4-digit run number + fullPath = fullfile(options.baseDataFolder, ds.sequence, dateParts{:}, runStr); + + if isfolder(fullPath) % only include valid directories + allPaths{end+1} = fullPath; + else + warning('Path does not exist: %s', fullPath); + end + end +end + +% Let user select a single path +set(0,'DefaultUicontrolFontSize',10); % increase default font size +[selectedIndex, tf] = listdlg('PromptString','Select a path to analyze:', ... + 'SelectionMode','single', ... + 'ListString', allPaths, ... + 'ListSize',[400, 300]); % width x height in pixels +if tf + options.folderPath = allPaths{selectedIndex}; % ✅ store in options + fprintf('Path set to selection: %s\n', options.folderPath); +else + error('No path selected. Aborting.'); +end + +%% ===== Collect Images and Launch Viewer ===== + +[od_imgs, scan_parameter_values, file_list] = Helper.collectODImages(options); + +Analyzer.runInteractiveODImageViewer(od_imgs, scan_parameter_values, file_list, options); \ No newline at end of file diff --git a/Data-Analyzer/+Scripts/BECToDropletsToStripes/runFullAnalysis.m b/Data-Analyzer/+Scripts/BECToDropletsToStripes/runFullAnalysis.m new file mode 100644 index 0000000..d8ec23d --- /dev/null +++ b/Data-Analyzer/+Scripts/BECToDropletsToStripes/runFullAnalysis.m @@ -0,0 +1,78 @@ +%% ===== BEC-Droplets-Stripes Settings ===== + +% Specify data location to run analysis on +dataSources = { + struct('sequence', 'StructuralPhaseTransition', ... + 'date', '2025/08/16', ... + 'runs', [8]) % specify run numbers as a string in "" or just as a numeric value +}; + +options = struct(); + +% File / paths +options.baseDataFolder = '//DyLabNAS/Data'; +options.measurementName = 'DropletsToStripes'; +scriptFullPath = mfilename('fullpath'); +options.saveDirectory = fileparts(scriptFullPath); + +% Camera / imaging +options.cam = 5; +options.angle = 0; +options.center = [1420, 2050]; +options.span = [200, 200]; +options.fraction = [0.1, 0.1]; +options.pixel_size = 5.86e-6; % in meters +options.magnification = 24.6; +options.removeFringes = false; +options.ImagingMode = 'HighIntensity'; +options.PulseDuration = 5e-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; + +% Scan parameter +options.scan_parameter = 'ps_rot_mag_fin_pol_angle'; + +switch options.measurementName + case 'BECToDroplets' + options.scan_reference_values = [2.40, 2.39, 2.38, 2.37, 2.35, 2.34, 2.32, 2.30, 2.28, 2.26, 2.24, 2.22, 2.2, 2.15, 2.10, 2.05, 2, 1.95, 1.90, 1.85, 1.8]; + options.titleString = 'BEC to Droplets'; + case 'BECToStripes' + options.scan_reference_values = [2.45, 2.44, 2.43, 2.42, 2.4, 2.39, 2.38, 2.37, 2.36, 2.35, 2.34, 2.32, 2.3, 2.28, 2.25, 2.2, 2.15, 2.10, 2.0, 1.90, 1.8]; + options.titleString = 'BEC to Stripes'; + case 'DropletsToStripes' + options.scan_reference_values = [0, 5, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 35, 40]; + options.titleString = 'Droplets to Stripes'; + case 'StripesToDroplets' + options.scan_reference_values = fliplr([0, 5, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 35, 40]); + options.titleString = 'Stripes to Droplets'; +end + +% Flags +options.skipNormalization = false; +options.skipUnshuffling = false; +options.skipPreprocessing = true; +options.skipMasking = true; +options.skipIntensityThresholding = true; +options.skipBinarization = true; +options.skipSaveFigures = false; +options.skipLivePlot = false; +options.showProgressBar = true; + +% Extras +options.font = 'Bahnschrift'; + +%% ===== Run Batch Analysis ===== +results_all = Helper.batchAnalyze(dataSources, options); diff --git a/Data-Analyzer/+Scripts/BECToStripes/plotImages.m b/Data-Analyzer/+Scripts/BECToStripes/plotImages.m index d35c330..a279a40 100644 --- a/Data-Analyzer/+Scripts/BECToStripes/plotImages.m +++ b/Data-Analyzer/+Scripts/BECToStripes/plotImages.m @@ -10,10 +10,10 @@ dataSources = { options = struct(); % File / paths -options.baseDataFolder = '//DyLabNAS/Data'; -options.savefileName = 'BECToDroplets'; -scriptFullPath = mfilename('fullpath'); -options.saveDirectory = fileparts(scriptFullPath); +options.baseDataFolder = '//DyLabNAS/Data'; +options.measurementName = 'BECToDroplets'; +scriptFullPath ´= mfilename('fullpath'); +options.saveDirectory = fileparts(scriptFullPath); % Camera / imaging options.cam = 5; @@ -45,7 +45,7 @@ options.zoom_size = 50; % Scan parameter options.scan_parameter = 'rot_mag_field'; -switch options.savefileName +switch options.measurementName case 'BECToDroplets' options.scan_reference_values = [2.40, 2.39, 2.38, 2.37, 2.35, 2.34, 2.32, 2.30, 2.28, 2.26, 2.24, 2.22, 2.2, 2.15, 2.10, 2.05, 2, 1.95, 1.90, 1.85, 1.8]; options.titleString = 'BEC to Droplets'; diff --git a/Data-Analyzer/+Scripts/BECToStripes/runFullAnalysis.m b/Data-Analyzer/+Scripts/BECToStripes/runFullAnalysis.m index e3aed21..fcb0524 100644 --- a/Data-Analyzer/+Scripts/BECToStripes/runFullAnalysis.m +++ b/Data-Analyzer/+Scripts/BECToStripes/runFullAnalysis.m @@ -10,10 +10,10 @@ dataSources = { options = struct(); % File / paths -options.baseDataFolder = '//DyLabNAS/Data'; -options.savefileName = 'BECToStripes'; -scriptFullPath = mfilename('fullpath'); -options.saveDirectory = fileparts(scriptFullPath); +options.baseDataFolder = '//DyLabNAS/Data'; +options.measurementName = 'BECToStripes'; +scriptFullPath = mfilename('fullpath'); +options.saveDirectory = fileparts(scriptFullPath); % Camera / imaging options.cam = 5; @@ -45,7 +45,7 @@ options.zoom_size = 50; % Scan parameter options.scan_parameter = 'rot_mag_field'; -switch options.savefileName +switch options.measurementName case 'BECToDroplets' options.scan_reference_values = [2.40, 2.39, 2.38, 2.37, 2.35, 2.34, 2.32, 2.30, 2.28, 2.26, 2.24, 2.22, 2.2, 2.15, 2.10, 2.05, 2, 1.95, 1.90, 1.85, 1.8]; options.titleString = 'BEC to Droplets';