Latest scripts - added one to compute TF radii of the unmodulated BEC as extracted from the simulations

This commit is contained in:
Karthik 2025-05-27 12:00:54 +02:00
parent c90c04249f
commit 99d9413530
2 changed files with 313 additions and 6 deletions

View File

@ -0,0 +1,170 @@
function [Rx, Ry] = extractThomasFermiRadii(folder_path, run_index, SuppressPlotFlag)
set(0,'defaulttextInterpreter','latex')
set(groot, 'defaultAxesTickLabelInterpreter','latex'); set(groot, 'defaultLegendInterpreter','latex');
format long
% Load data
Data = load(sprintf(horzcat(folder_path, '/Run_%03i/psi_gs.mat'),run_index),'psi','Params','Transf','Observ');
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
% Axes scaling and coordinates in micrometers
x = Transf.x * Params.l0 * 1e6;
y = Transf.y * Params.l0 * 1e6;
z = Transf.z * Params.l0 * 1e6;
dx = x(2)-x(1); dy = y(2)-y(1); dz = z(2)-z(1);
% Compute probability density |psi|^2
n = abs(psi).^2;
nxz = squeeze(trapz(n*dy,2));
nyz = squeeze(trapz(n*dx,1));
nxy = squeeze(trapz(n*dz,3));
state = nxy';
% Fit
fitResult = fitThomasFermiProfile2D(state, x, y);
Rx = fitResult.Rx;
Ry = fitResult.Ry;
if ~SuppressPlotFlag
fig = figure(1);
clf
set(gcf,'Position', [100, 100, 1800, 500])
t = tiledlayout(1, 3, 'TileSpacing', 'compact', 'Padding', 'compact');
% Original density plot
nexttile;
plot1 = pcolor(x, y, state);
set(plot1, 'EdgeColor', 'none');
colormap(gca, Helper.Colormaps.plasma())
colorbar;
xlabel('$x$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14)
ylabel('$y$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14)
title('Original Density', 'Interpreter', 'latex', 'FontSize', 16)
axis square;
set(gca, 'FontSize', 14);
% Fitted TF profile plot
nexttile;
plot2 = pcolor(x, y, fitResult.fittedDensity);
set(plot2, 'EdgeColor', 'none');
colormap(gca, Helper.Colormaps.plasma())
colorbar;
xlabel('$x$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14)
ylabel('$y$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14)
title(sprintf('TF Fit: $R_x = %.2f\\,\\mu\\mathrm{m}$, $R_y = %.2f\\,\\mu\\mathrm{m}$', Rx, Ry), ...
'Interpreter', 'latex', 'FontSize', 16)
axis square;
set(gca, 'FontSize', 14);
% 1D cuts through center
nexttile;
% Find index of center
[~, ix0] = min(abs(x - fitResult.x0));
[~, iy0] = min(abs(y - fitResult.y0));
% X-cut: density along y = y0
orig_cut_x = state(iy0, :);
fit_cut_x = fitResult.fittedDensity(iy0, :);
% Y-cut: density along x = x0
orig_cut_y = state(:, ix0);
fit_cut_y = fitResult.fittedDensity(:, ix0);
% Plot both cuts
plot(x, orig_cut_x, 'w-', 'LineWidth', 1.5); hold on
plot(x, fit_cut_x, 'r--', 'LineWidth', 1.5)
plot(y, orig_cut_y, 'w-', 'LineWidth', 1.5)
plot(y, fit_cut_y, 'r--', 'LineWidth', 1.5)
hold off
axis square;
legend({'$n(x)$', '$n_{\mathrm{TF}}(x)$', '$n(y)$', '$n_{\mathrm{TF}}(y)$'}, ...
'Interpreter', 'latex', 'FontSize', 12, 'Location', 'northeast')
xlabel('$x$ or $y$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14)
ylabel('Density', 'Interpreter', 'latex', 'FontSize', 14)
title('1-D Cut', 'Interpreter', 'latex', 'FontSize', 16)
grid on
set(gca, 'FontSize', 14)
end
end
function fitResult = fitThomasFermiProfile2D(state, xgrid, ygrid)
% Fits a 2D Thomas-Fermi profile to the input BEC state
%
% Input:
% state - 2D array (real density or complex wavefunction)
% xgrid - vector of x-coordinates (size must match size(state,2))
% ygrid - vector of y-coordinates (size must match size(state,1))
%
% Output (struct):
% fitResult.fittedDensity - 2D array of the fitted TF profile
% fitResult.n0 - peak density
% fitResult.x0, y0 - center coordinates
% fitResult.Rx, Ry - Thomas-Fermi radii
% fitResult.params - parameter vector [n0, x0, y0, Rx, Ry]
% Compute density if input is a wavefunction
if ~isreal(state)
density = abs(state).^2;
else
density = state;
end
% Set up grid
[X, Y] = meshgrid(xgrid, ygrid);
xy = [X(:), Y(:)];
n = density(:);
% Mask: ignore near-zero values
mask = n > max(n)*1e-3;
xy = xy(mask, :);
n = n(mask);
% Initial guess: [n0, x0, y0, Rx, Ry]
n0_guess = max(n);
x0_guess = mean(xgrid(:));
y0_guess = mean(ygrid(:));
Rx_guess = (max(xgrid)-min(xgrid)) / 2;
Ry_guess = (max(ygrid)-min(ygrid)) / 2;
p0 = [n0_guess, x0_guess, y0_guess, Rx_guess, Ry_guess];
% Bounds
lb = [0, min(xgrid), min(ygrid), 0, 0];
ub = [Inf, max(xgrid), max(ygrid), Inf, Inf];
% TF 2D function
tf2d = @(p, xy) max(0, p(1) * (1 - ((xy(:,1)-p(2)).^2 / p(4)^2 + (xy(:,2)-p(3)).^2 / p(5)^2)));
% Fit
options = optimoptions('lsqcurvefit','Display','off');
pfit = lsqcurvefit(tf2d, p0, xy, n, lb, ub, options);
% Evaluate fitted profile over full grid
fittedDensity = reshape(tf2d(pfit, [X(:), Y(:)]), size(X));
% Return result
fitResult.fittedDensity = fittedDensity;
fitResult.n0 = pfit(1);
fitResult.x0 = pfit(2);
fitResult.y0 = pfit(3);
fitResult.Rx = pfit(4);
fitResult.Ry = pfit(5);
fitResult.params = pfit;
end

View File

@ -572,7 +572,7 @@ DropletNumber = Scripts.identifyAndCountDroplets(SaveDirectory, JobNum
Radius = 2;
PeakThreshold = 6E3;
JobNumber = 0;
TitleString = "[ \omega_x, \omega_y, \omega_z ] = 2 \pi \times [ 50, 20, 150 ] Hz; \theta = 0";
TitleString = "[ \omega_x, \omega_y, \omega_z ] = 2 \pi \times [ 50, 20, 150 ] Hz; \theta = 0^\circ";
SuppressPlotFlag = true;
SCATTERING_LENGTH_RANGE = [85.21 86.25 87.29 88.33 89.38];
@ -632,6 +632,144 @@ legend(legendEntries, 'Interpreter', 'tex', 'FontSize', 12, 'Location', 'bestout
grid on;
hold off;
%% Plot TF radii of unmodulated states
% Parameters
JobNumber = 0;
TitleString = "[ \omega_x, \omega_y, \omega_z ] = 2 \pi \times [ 50, 20, 150 ] Hz; \theta = 40^\circ";
SuppressPlotFlag = true;
SCATTERING_LENGTH_RANGE = [97.71];
NUM_ATOMS_LIST = [50000 54545 59091 63636 68182 72727 77273 81818 86364 90909 95455 100000 304167 508333 712500 916667 1120833 1325000 1529167 1733333 1937500 2141667 2345833 2550000 2754167 2958333 3162500 3366667 3570833 3775000 3979167 4183333 4387500 4591667 4795833 5000000];
% Loop over scattering lengths
for j = 1:length(SCATTERING_LENGTH_RANGE)
aS = SCATTERING_LENGTH_RANGE(j);
% Format scattering length in scientific notation with 6 decimal places
aS_string = sprintf('%.6e', aS);
% Construct base directory for this aS
baseDir = ['D:/Results - Numerics/Data_Full3D/PhaseDiagram/ImagTimePropagation/Theta40/aS_' ...
aS_string '_theta_040_phi_000_N_'];
% Preallocate results for this curve: rows = N, cols = [Rx, Ry]
TF_Radii = zeros(length(NUM_ATOMS_LIST), 2);
% Loop over all atom numbers
for i = 1:length(NUM_ATOMS_LIST)
N = NUM_ATOMS_LIST(i);
SaveDirectory = [baseDir num2str(N)];
% Extract both Rx and Ry
try
[Rx, Ry] = Scripts.extractThomasFermiRadii(SaveDirectory, JobNumber, SuppressPlotFlag);
catch ME
warning(ME.message)
Rx = NaN;
Ry = NaN;
end
% Store as row [Rx, Ry]
TF_Radii(i, :) = [Rx, Ry];
end
end
% Plot curve
% Prepare figure
fig = figure(1);
clf
set(gcf,'Position', [100, 100, 1200, 500])
t = tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'compact');
% Color order for better visibility
colors = lines(length(SCATTERING_LENGTH_RANGE));
% Original density plot
nexttile;
plot(NUM_ATOMS_LIST, TF_Radii(:, 1), 'o-', ...
'Color', colors(j, :), 'LineWidth', 1.5);
% Store legend entry
legendEntries{j} = ['a_s = ' num2str(aS) 'a_o'];
% Finalize plot
xlabel('Number of Atoms', 'Interpreter', 'tex', 'FontSize', 16);
ylabel('TF Radius - X ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 16);
legend(legendEntries,'Interpreter', 'tex', 'FontSize', 12, 'Location', 'bestoutside');
axis square
grid on;
nexttile
plot(NUM_ATOMS_LIST, TF_Radii(:, 2), 'o-', ...
'Color', colors(j, :), 'LineWidth', 1.5);
% Store legend entry
legendEntries{j} = ['a_s = ' num2str(aS) 'a_o'];
% Finalize plot
xlabel('Number of Atoms', 'Interpreter', 'tex', 'FontSize', 16);
ylabel('TF Radius - Y ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 16);
legend(legendEntries,'Interpreter', 'tex', 'FontSize', 12, 'Location', 'bestoutside');
axis square
grid on;
sgtitle(TitleString, 'Interpreter', 'tex', 'FontSize', 18);
%% Overlay curves
% File list and associated theta labels
fileList = {'TFFermi_Theta0.mat', 'TFFermi_Theta20.mat', 'TFFermi_Theta40.mat'};
thetaLabels = {'\theta = 0^\circ', '\theta = 20^\circ', '\theta = 40^\circ'};
% Set up figure
fig = figure(1);
clf
set(gcf,'Position', [100, 100, 1200, 500])
t = tiledlayout(1, 2, 'TileSpacing', 'compact', 'Padding', 'compact');
% Color order for visibility
colors = lines(length(fileList));
legendEntries = cell(1, length(fileList));
% Loop over files
for j = 1:length(fileList)
data = load(fileList{j});
aS = data.SCATTERING_LENGTH_RANGE; % scalar
NUM_ATOMS_LIST = data.NUM_ATOMS_LIST;
TF_Radii = data.TF_Radii; % [N x 2]
% Store legend entry (can choose to include theta or a_s)
legendEntries{j} = sprintf('%s, a_s = %.2f a_0', thetaLabels{j}, aS);
% Plot Rx (TF_Radii(:,1))
nexttile(1);
plot(NUM_ATOMS_LIST, TF_Radii(:, 1), 'o-', ...
'Color', colors(j, :), 'LineWidth', 1.5); hold on;
% Plot Ry (TF_Radii(:,2))
nexttile(2);
plot(NUM_ATOMS_LIST, TF_Radii(:, 2), 'o-', ...
'Color', colors(j, :), 'LineWidth', 1.5); hold on;
end
% Finalize Rx plot
nexttile(1);
xlabel('Number of Atoms', 'Interpreter', 'tex', 'FontSize', 16);
ylabel('TF Radius - X ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 16);
legend(legendEntries, 'Interpreter', 'tex', 'FontSize', 12, 'Location', 'bestoutside');
axis square
grid on;
% Finalize Ry plot
nexttile(2);
xlabel('Number of Atoms', 'Interpreter', 'tex', 'FontSize', 16);
ylabel('TF Radius - Y ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 16);
legend(legendEntries, 'Interpreter', 'tex', 'FontSize', 12, 'Location', 'bestoutside');
axis square
grid on;
% Add super title
sgtitle('[ \omega_x, \omega_y, \omega_z ] = 2 \pi \times [ 50, 20, 150 ] Hz', ...
'Interpreter', 'tex', 'FontSize', 18);
%% Generate lists
% Set display format
@ -695,6 +833,7 @@ data = {
'aS_8.729000e+01_theta_000_phi_000_N_95455'
'aS_8.729000e+01_theta_000_phi_000_N_3775000'
'aS_9.250000e+01_theta_000_phi_000_N_5000000'
'aS_9.771000e+01_theta_020_phi_000_N_3775000'
};
% Preallocate arrays
@ -788,12 +927,12 @@ Scripts.plotSmoothedContourOnPhaseDiagram(M, SCATTERING_LENGTH_RANGE, NUM_ATOMS_
'interpMethod', 'pchip', 'showPoints', true);
%% Plot with interpolated phase boundary
load('./Results/Data_Full3D/PhaseDiagramTilted_Theta_20.mat')
load('./Results/Data_Full3D/PhaseDiagramTilted_Theta_40.mat')
PhaseDiagramMatrix = M;
ScatteringLengths = SCATTERING_LENGTH_RANGE;
NumberOfAtoms = NUM_ATOMS_LIST;
PhaseBoundary = load("./Results/Data_Full3D/BoundaryPoints/PhaseBoundary_Tilted_Theta20.mat");
TitleString = "[ \omega_x, \omega_y, \omega_z ] = 2 \pi \times [ 50, 20, 150 ] Hz; \theta = 20^\circ";
PhaseBoundary = load("./Results/Data_Full3D/BoundaryPoints/PhaseBoundary_Tilted_Theta40.mat");
TitleString = "[ \omega_x, \omega_y, \omega_z ] = 2 \pi \times [ 50, 20, 150 ] Hz; \theta = 40^\circ";
Scripts.plotPhaseDiagramWithBoundaries(M, SCATTERING_LENGTH_RANGE, NUM_ATOMS_LIST, PhaseBoundary, TitleString);
@ -831,5 +970,3 @@ else
disp('The state is not modulated');
end