diff --git a/Dipolar-Gas-Simulator/+Scripts/conductFourierAnalysis.m b/Dipolar-Gas-Simulator/+Scripts/conductFourierAnalysis.m new file mode 100644 index 0000000..0a49e39 --- /dev/null +++ b/Dipolar-Gas-Simulator/+Scripts/conductFourierAnalysis.m @@ -0,0 +1,296 @@ +function [spectral_weight, g2, theta_vals] = conductFourierAnalysis(folder_path, run_index, N_bins, Threshold, Sigma, SuppressPlotFlag) + + arguments + folder_path (1,:) char + run_index (1,:) {mustBeNumeric,mustBeReal} + N_bins (1,:) {mustBeNumeric,mustBeReal} + Threshold (1,:) {mustBeNumeric,mustBeReal} + Sigma (1,:) {mustBeNumeric,mustBeReal} + SuppressPlotFlag (1,:) logical = true + end + + set(0,'defaulttextInterpreter','latex') + set(groot, 'defaultAxesTickLabelInterpreter','latex'); set(groot, 'defaultLegendInterpreter','latex'); + + % Load data + Data = load(fullfile(fullfile(folder_path, sprintf('Run_%03i', run_index)), 'psi_gs.mat'), 'psi', 'Transf', 'Observ', 'Params'); + + Params = Data.Params; + Transf = Data.Transf; + Observ = Data.Observ; + + if isgpuarray(Data.psi) + psi = gather(Data.psi); + else + psi = Data.psi; + end + if isgpuarray(Data.Observ.residual) + Observ.residual = gather(Data.Observ.residual); + else + Observ.residual = Data.Observ.residual; + end + + alpha = Params.theta; + + % Axes scaling and coordinates in micrometers + x = Transf.x * Params.l0 * 1e6; + y = Transf.y * Params.l0 * 1e6; + z = Transf.z * Params.l0 * 1e6; + + dz = z(2)-z(1); + + % Calculate frequency increment (frequency axes) + Nx = length(x); % grid size along X + Ny = length(y); % grid size along Y + dx = mean(diff(x)); % real space increment in the X direction (in micrometers) + dy = mean(diff(y)); % real space increment in the Y direction (in micrometers) + dvx = 1 / (Nx * dx); % reciprocal space increment in the X direction (in micrometers^-1) + dvy = 1 / (Ny * dy); % reciprocal space increment in the Y direction (in micrometers^-1) + + % Create the frequency axes + vx = (-Nx/2:Nx/2-1) * dvx; % Frequency axis in X (micrometers^-1) + vy = (-Ny/2:Ny/2-1) * dvy; % Frequency axis in Y (micrometers^-1) + + % Calculate maximum frequencies + % kx_max = pi / dx; + % ky_max = pi / dy; + + % Generate reciprocal axes + % kx = linspace(-kx_max, kx_max * (Nx-2)/Nx, Nx); + % ky = linspace(-ky_max, ky_max * (Ny-2)/Ny, Ny); + + % Create the Wavenumber axes + kx = 2*pi*vx; % Wavenumber axis in X + ky = 2*pi*vy; % Wavenumber axis in Y + + % Compute probability density |psi|^2 + n = abs(psi).^2; + + nxy = squeeze(trapz(n*dz,3)); + + skipPreprocessing = true; + skipMasking = true; + skipIntensityThresholding = true; + skipBinarization = true; + + %% Extract Spectral Weight and g2 + + IMG = nxy; + + [IMGFFT, ~] = computeFourierTransform(IMG, skipPreprocessing, skipMasking, skipIntensityThresholding, skipBinarization); + + [theta_vals, S_theta] = computeNormalizedAngularSpectralDistribution(IMGFFT, 10, 35, N_bins, Threshold, Sigma); + + spectral_weight = trapz(theta_vals, S_theta); + + g2 = zeros(1, N_bins); % Preallocate + + for dtheta = 0:N_bins-1 + profile = S_theta; + profile_shifted = circshift(profile, -dtheta, 2); + + num = mean(profile .* profile_shifted); + denom = mean(profile)^2; + + g2(dtheta+1) = num / denom - 1; + end + + if ~SuppressPlotFlag + figure(1); + clf + set(gcf,'Position',[500 100 1000 800]) + t = tiledlayout(2, 2, 'TileSpacing', 'compact', 'Padding', 'compact'); % 1x4 grid + font = 'Bahnschrift'; + % Display the cropped OD image + ax1 = nexttile; + plotxy = pcolor(x,y,IMG'); + set(plotxy, 'EdgeColor', 'none'); + % Define normalized positions (relative to axis limits) + x_offset = 0.025; % 5% offset from the edges + y_offset = 0.025; % 5% offset from the edges + % Top-right corner (normalized axis coordinates) + hText = text(1 - x_offset, 1 - y_offset, ... + ['\alpha = ', num2str(rad2deg(alpha), '%.1f'), '^\circ'], ... + 'Color', 'white', 'FontWeight', 'bold', ... + 'Interpreter', 'tex', 'FontSize', 20, ... + 'Units', 'normalized', ... + 'HorizontalAlignment', 'right', ... + 'VerticalAlignment', 'top'); + axis square; + hcb = colorbar; + hcb.Label.Interpreter = 'latex'; + colormap(gca, Helper.Colormaps.plasma()) + set(gca, 'FontSize', 14); % For tick labels only + hL = ylabel(hcb, 'Column Density'); + hXLabel = xlabel('$x$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14); + hYLabel = ylabel('$y$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14); + hTitle = title('$|\Psi(x,y)|^2$', 'Interpreter', 'latex', 'FontSize', 14) ; + set(hText, 'FontName', font) + set([hXLabel, hYLabel, hL], 'FontSize', 14) + set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold'); % Set font and size for title + + + % Plot the power spectrum + nexttile; + imagesc(kx, ky, log(1 + abs(IMGFFT).^2)); + axis square; + hcb = colorbar; + colormap(gca, Helper.Colormaps.plasma()) + set(gca, 'FontSize', 14); % For tick labels only + set(gca,'YDir','normal') + hXLabel = xlabel('k_x', 'Interpreter', 'tex'); + hYLabel = ylabel('k_y', 'Interpreter', 'tex'); + hTitle = title('Power Spectrum - S(k_x,k_y)', 'Interpreter', 'tex'); + set([hXLabel, hYLabel], 'FontName', font) + set([hXLabel, hYLabel], 'FontSize', 14) + set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold'); % Set font and size for title + + % Plot the angular distribution + nexttile + plot(theta_vals/pi, S_theta,'Linewidth',2); + set(gca, 'FontSize', 14); % For tick labels only + hXLabel = xlabel('\theta/\pi [rad]', 'Interpreter', 'tex'); + hYLabel = ylabel('Normalized magnitude (a.u.)', 'Interpreter', 'tex'); + hTitle = title('Angular Spectral Distribution - S(\theta)', 'Interpreter', 'tex'); + set([hXLabel, hYLabel], 'FontName', font) + set([hXLabel, hYLabel], 'FontSize', 14) + set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold'); % Set font and size for title + grid on + + nexttile + plot(theta_vals/pi, g2, 'o-', 'LineWidth', 1.2, 'MarkerSize', 5); + set(gca, 'FontSize', 14); + ylim([-1.5 3.0]); % Set y-axis limits here + hXLabel = xlabel('$\delta\theta / \pi$', 'Interpreter', 'latex'); + hYLabel = ylabel('$g^{(2)}(\delta\theta)$', 'Interpreter', 'latex'); + hTitle = title('Autocorrelation', 'Interpreter', 'tex'); + set([hXLabel, hYLabel], 'FontName', font) + set([hXLabel, hYLabel], 'FontSize', 14) + set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold'); % Set font and size for title + grid on; + end +end + +%% Helper Functions +function [IMGFFT, IMGPR] = computeFourierTransform(I, skipPreprocessing, skipMasking, skipIntensityThresholding, skipBinarization) + % computeFourierSpectrum - Computes the 2D Fourier power spectrum + % of binarized and enhanced lattice image features, with optional central mask. + % + % Inputs: + % I - Grayscale or RGB image matrix + % + % Output: + % F_mag - 2D Fourier power spectrum (shifted) + + if ~skipPreprocessing + % Preprocessing: Denoise + filtered = imgaussfilt(I, 10); + IMGPR = I - filtered; % adjust sigma as needed + else + IMGPR = I; + end + + if ~skipMasking + [rows, cols] = size(IMGPR); + [X, Y] = meshgrid(1:cols, 1:rows); + % Elliptical mask parameters + cx = cols / 2; + cy = rows / 2; + + % Shifted coordinates + x = X - cx; + y = Y - cy; + + % Ellipse semi-axes + rx = 0.4 * cols; + ry = 0.2 * rows; + + % Rotation angle in degrees -> radians + theta_deg = 30; % Adjust as needed + theta = deg2rad(theta_deg); + + % Rotated ellipse equation + cos_t = cos(theta); + sin_t = sin(theta); + + x_rot = (x * cos_t + y * sin_t); + y_rot = (-x * sin_t + y * cos_t); + + ellipseMask = (x_rot.^2) / rx^2 + (y_rot.^2) / ry^2 <= 1; + + % Apply cutout mask + IMGPR = IMGPR .* ellipseMask; + end + + if ~skipIntensityThresholding + % Apply global intensity threshold mask + intensity_thresh = 0.20; + intensity_mask = IMGPR > intensity_thresh; + IMGPR = IMGPR .* intensity_mask; + end + + if ~skipBinarization + % Adaptive binarization and cleanup + IMGPR = imbinarize(IMGPR, 'adaptive', 'Sensitivity', 0.0); + IMGPR = imdilate(IMGPR, strel('disk', 2)); + IMGPR = imerode(IMGPR, strel('disk', 1)); + IMGPR = imfill(IMGPR, 'holes'); + F = fft2(double(IMGPR)); % Compute 2D Fourier Transform + IMGFFT = abs(fftshift(F))'; % Shift zero frequency to center + else + F = fft2(double(IMGPR)); % Compute 2D Fourier Transform + IMGFFT = abs(fftshift(F))'; % Shift zero frequency to center + end +end + +function [theta_vals, S_theta] = computeNormalizedAngularSpectralDistribution(IMGFFT, r_min, r_max, num_bins, threshold, sigma) + % Apply threshold to isolate strong peaks + IMGFFT(IMGFFT < threshold) = 0; + + % Prepare polar coordinates + [ny, nx] = size(IMGFFT); + [X, Y] = meshgrid(1:nx, 1:ny); + cx = ceil(nx/2); + cy = ceil(ny/2); + R = sqrt((X - cx).^2 + (Y - cy).^2); + Theta = atan2(Y - cy, X - cx); % range [-pi, pi] + + % Choose radial band + radial_mask = (R >= r_min) & (R <= r_max); + + % Initialize the angular structure factor array + S_theta = zeros(1, num_bins); % Pre-allocate for 180 angle bins + % Define the angle values for the x-axis + theta_vals = linspace(0, pi, num_bins); + + % Loop through each angle bin + for i = 1:num_bins + angle_start = (i-1) * pi / num_bins; + angle_end = i * pi / num_bins; + + % Define a mask for the given angle range + angle_mask = (Theta >= angle_start & Theta < angle_end); + + bin_mask = radial_mask & angle_mask; + + % Extract the Fourier components for the given angle + fft_angle = IMGFFT .* bin_mask; + + % Integrate the Fourier components over the radius at the angle + S_theta(i) = sum(sum(abs(fft_angle).^2)); % sum of squared magnitudes + end + + % Create a 1D Gaussian kernel + half_width = ceil(3 * sigma); + x = -half_width:half_width; + gauss_kernel = exp(-x.^2 / (2 * sigma^2)); + gauss_kernel = gauss_kernel / sum(gauss_kernel); % normalize + + % Apply convolution (circular padding to preserve periodicity) + S_theta = conv([S_theta(end-half_width+1:end), S_theta, S_theta(1:half_width)], gauss_kernel, 'same'); + S_theta = S_theta(half_width+1:end-half_width); % crop back to original size + + % Normalize to 1 + S_theta = S_theta / max(S_theta); +end + diff --git a/Dipolar-Gas-Simulator/+Scripts/run_hybrid_worker.m b/Dipolar-Gas-Simulator/+Scripts/run_hybrid_worker.m index 8cf74e6..11fa382 100644 --- a/Dipolar-Gas-Simulator/+Scripts/run_hybrid_worker.m +++ b/Dipolar-Gas-Simulator/+Scripts/run_hybrid_worker.m @@ -48,7 +48,7 @@ function run_hybrid_worker(batchParams, batchIdx) OptionsStruct.MaxIterationsForGD = 15000; OptionsStruct.TimeStepSize = 1E-3; % in s OptionsStruct.MinimumTimeStepSize = 1E-6; % in s - OptionsStruct.TimeCutOff = 2E5; % in s + OptionsStruct.TimeCutOff = 1E6; % in s OptionsStruct.EnergyTolerance = 5E-10; OptionsStruct.ResidualTolerance = 1E-05; OptionsStruct.NoiseScaleFactor = 0.010; @@ -77,4 +77,4 @@ function run_hybrid_worker(batchParams, batchIdx) end delete(pool); -end +end \ No newline at end of file diff --git a/Dipolar-Gas-Simulator/+Scripts/run_locally.m b/Dipolar-Gas-Simulator/+Scripts/run_locally.m index bcb7984..8362af4 100644 --- a/Dipolar-Gas-Simulator/+Scripts/run_locally.m +++ b/Dipolar-Gas-Simulator/+Scripts/run_locally.m @@ -1066,3 +1066,69 @@ else disp('The state is not modulated'); end +%% Plot Spectral weight and g2 across transition for simulated data +N_atoms = 5E5; +a_s = 95.62; +phi_deg = 0.0; +alpha_vals = [0.0 5.0 10.0 15.0 20.0 25.0 30.0 35.0 40.0 45.0]; + +N_alpha = length(alpha_vals); +N_bins = 90; +Threshold = 75; +Sigma = 2; + +spectral_weights = zeros(size(alpha_vals)); +g2_all = zeros(N_alpha, N_bins); +theta_vals_all = zeros(N_alpha, N_bins); + +JobNumber = 1; +SuppressPlotFlag = true; + +for i = 1:N_alpha + folderName = sprintf('aS_%03d_theta_%03d_phi_%03d_N_%d', a_s, alpha_vals(i), phi_deg, N_atoms); + SaveDirectory = fullfile('D:/Results - Numerics/Data_Full3D/PhaseTransition/DTS/', folderName); + [spectral_weight, g2, theta_vals] = Scripts.conductFourierAnalysis(SaveDirectory, JobNumber, N_bins, Threshold, Sigma, SuppressPlotFlag); + spectral_weights(i) = spectral_weight; % Store the spectral weight for the current alpha value + g2_all(i, :) = g2; % Store the g2 results for the current alpha value + theta_vals_all(i, :) = theta_vals; +end + +figure(2); +set(gcf,'Position',[100 100 950 750]) +font = 'Bahnschrift'; +plot(alpha_vals, spectral_weights, 'o--', ... + 'LineWidth', 1.5, 'MarkerSize', 6); +set(gca, 'FontSize', 14); % For tick labels only +hXLabel = xlabel('\alpha (degrees)', 'Interpreter', 'tex'); +hYLabel = ylabel('Spectral Weight', 'Interpreter', 'tex'); +hTitle = title('Change across transition', 'Interpreter', 'tex'); +set([hXLabel, hYLabel], 'FontName', font) +set([hXLabel, hYLabel], 'FontSize', 14) +set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold'); % Set font and size for title +grid on + +figure(3); +set(gcf,'Position',[100 100 950 750]) +font = 'Bahnschrift'; +legend_entries = cell(N_alpha, 1); +theta_vals = theta_vals_all(1, :); +cmap = sky(N_alpha); % Generate a colormap with enough unique colors +hold on + +for i = 1:N_alpha + plot(theta_vals/pi, g2_all(i, :), ... + 'o-', 'Color', cmap(i,:), 'LineWidth', 1.2, ... + 'MarkerSize', 5); + legend_entries{i} = sprintf('$\\alpha = %g^\\circ$', alpha_vals(i)); +end +set(gca, 'FontSize', 14); +ylim([-1.5 10.0]); % Set y-axis limits here +hXLabel = xlabel('$\delta\theta / \pi$', 'Interpreter', 'latex'); +hYLabel = ylabel('$g^{(2)}(\delta\theta)$', 'Interpreter', 'latex'); +hTitle = title('Transition from Droplets to Stripes', 'Interpreter', 'tex'); +legend(legend_entries, 'Interpreter', 'latex', 'Location', 'bestoutside'); +set([hXLabel, hYLabel], 'FontName', font) +set([hXLabel, hYLabel], 'FontSize', 14) +set(hTitle, 'FontName', font, 'FontSize', 16, 'FontWeight', 'bold'); % Set font and size for title +grid on; + diff --git a/Dipolar-Gas-Simulator/+Scripts/run_on_cluster.m b/Dipolar-Gas-Simulator/+Scripts/run_on_cluster.m index 5a16eab..62efda6 100644 --- a/Dipolar-Gas-Simulator/+Scripts/run_on_cluster.m +++ b/Dipolar-Gas-Simulator/+Scripts/run_on_cluster.m @@ -46,13 +46,13 @@ function run_on_cluster(batchParams, batchIdx) OptionsStruct.MaxIterationsForGD = 15000; OptionsStruct.TimeStepSize = 1E-3; OptionsStruct.MinimumTimeStepSize = 1E-6; - OptionsStruct.TimeCutOff = 5E5; + OptionsStruct.TimeCutOff = 2E5; OptionsStruct.EnergyTolerance = 5E-08; OptionsStruct.ResidualTolerance = 1E-05; OptionsStruct.NoiseScaleFactor = 0.010; OptionsStruct.PlotLive = false; - OptionsStruct.JobNumber = k; + OptionsStruct.JobNumber = 0; OptionsStruct.RunOnGPU = true; OptionsStruct.SaveData = true; OptionsStruct.SaveDirectory = saveDir;