New analysis script - characterization of lattice geometries in real space
This commit is contained in:
parent
e256caa0dc
commit
4416bc58b6
179
Data-Analyzer/computeBondOrderParameters.m
Normal file
179
Data-Analyzer/computeBondOrderParameters.m
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
function [psi_global, order_ratio, Npoints] = computeBondOrderParameters(I)
|
||||||
|
% Analyze bond-orientational order in a 2D image, restricted to a circular region
|
||||||
|
%
|
||||||
|
% Input:
|
||||||
|
% I - 2D grayscale image (double or uint8)
|
||||||
|
%
|
||||||
|
% Outputs:
|
||||||
|
% psi_global - struct with fields psi2, psi4, psi6 (⟨|ψₙ|⟩ values)
|
||||||
|
% order_ratio - scalar = ⟨|ψ₆|⟩ / ⟨|ψ₂|⟩
|
||||||
|
% Npoints - number of detected points used
|
||||||
|
|
||||||
|
%% Step 1: Create mask
|
||||||
|
[Ny, Nx] = size(I);
|
||||||
|
% Parameters for elliptical mask
|
||||||
|
a = Nx / 2.25; % Semi-major axis (x-direction)
|
||||||
|
b = Ny / 2.25; % Semi-minor axis (y-direction)
|
||||||
|
theta_deg = 0; % Rotation angle in degrees
|
||||||
|
theta = deg2rad(theta_deg); % Convert to radians
|
||||||
|
|
||||||
|
% Meshgrid coordinates
|
||||||
|
[Ny, Nx] = size(I);
|
||||||
|
[X, Y] = meshgrid(1:Nx, 1:Ny);
|
||||||
|
cx = Nx / 2;
|
||||||
|
cy = Ny / 2;
|
||||||
|
|
||||||
|
% Shifted coordinates
|
||||||
|
Xc = X - cx;
|
||||||
|
Yc = Y - cy;
|
||||||
|
|
||||||
|
% Rotate coordinates
|
||||||
|
Xr = cos(theta) * Xc + sin(theta) * Yc;
|
||||||
|
Yr = -sin(theta) * Xc + cos(theta) * Yc;
|
||||||
|
|
||||||
|
% Elliptical mask equation
|
||||||
|
mask = (Xr / a).^2 + (Yr / b).^2 <= 1;
|
||||||
|
|
||||||
|
% Apply mask to image
|
||||||
|
I = double(I) .* mask;
|
||||||
|
|
||||||
|
%% Step 2: Detect local maxima within mask
|
||||||
|
I_smooth = imgaussfilt(I, 2);
|
||||||
|
peaks = imregionalmax(I_smooth);
|
||||||
|
[y, x] = find(peaks);
|
||||||
|
Npoints = length(x);
|
||||||
|
|
||||||
|
if Npoints < 6
|
||||||
|
warning('Too few points (%d) to compute bond order meaningfully.', Npoints);
|
||||||
|
psi_global = struct('psi2', NaN, 'psi4', NaN, 'psi6', NaN);
|
||||||
|
order_ratio = NaN;
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
%% Step 3: Try Delaunay triangulation
|
||||||
|
use_kNN = false;
|
||||||
|
try
|
||||||
|
tri = delaunay(x, y);
|
||||||
|
catch
|
||||||
|
warning('Delaunay triangulation failed - falling back to kNN neighbor search');
|
||||||
|
use_kNN = true;
|
||||||
|
end
|
||||||
|
|
||||||
|
% 3) Find neighbors
|
||||||
|
neighbors = cell(Npoints, 1);
|
||||||
|
if ~use_kNN
|
||||||
|
% Use Delaunay neighbors
|
||||||
|
for i = 1:size(tri,1)
|
||||||
|
for j = 1:3
|
||||||
|
idx = tri(i,j);
|
||||||
|
nbrs = tri(i,[1 2 3] ~= j);
|
||||||
|
neighbors{idx} = unique([neighbors{idx}, nbrs]);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
% Use kNN neighbors (e.g. k=6)
|
||||||
|
k = min(6, Npoints-1);
|
||||||
|
pts = [x, y];
|
||||||
|
[idx, ~] = knnsearch(pts, pts, 'K', k+1); % +1 for self
|
||||||
|
for j = 1:Npoints
|
||||||
|
neighbors{j} = idx(j, 2:end);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
% 4) Compute bond order parameters
|
||||||
|
psi2 = zeros(Npoints,1);
|
||||||
|
psi4 = zeros(Npoints,1);
|
||||||
|
psi6 = zeros(Npoints,1);
|
||||||
|
|
||||||
|
for j = 1:Npoints
|
||||||
|
nbrs = neighbors{j};
|
||||||
|
nbrs(nbrs == j) = [];
|
||||||
|
if isempty(nbrs)
|
||||||
|
continue;
|
||||||
|
end
|
||||||
|
angles = atan2(y(nbrs) - y(j), x(nbrs) - x(j));
|
||||||
|
psi2(j) = abs(mean(exp(1i * 2 * angles)));
|
||||||
|
psi4(j) = abs(mean(exp(1i * 4 * angles)));
|
||||||
|
psi6(j) = abs(mean(exp(1i * 6 * angles)));
|
||||||
|
end
|
||||||
|
|
||||||
|
psi_global.psi2 = mean(psi2);
|
||||||
|
psi_global.psi4 = mean(psi4);
|
||||||
|
psi_global.psi6 = mean(psi6);
|
||||||
|
|
||||||
|
order_ratio = psi_global.psi6 / psi_global.psi2;
|
||||||
|
|
||||||
|
% --- After neighbors are found and (x,y) extracted ---
|
||||||
|
|
||||||
|
% 1) Angular distribution of all bonds pooled from all points
|
||||||
|
all_angles = [];
|
||||||
|
for j = 1:Npoints
|
||||||
|
nbrs = neighbors{j};
|
||||||
|
nbrs(nbrs == j) = [];
|
||||||
|
if isempty(nbrs), continue; end
|
||||||
|
angles = atan2(y(nbrs)-y(j), x(nbrs)-x(j));
|
||||||
|
all_angles = [all_angles; angles(:)];
|
||||||
|
end
|
||||||
|
|
||||||
|
% Histogram of bond angles over [-pi, pi]
|
||||||
|
nbins = 36; % 10 degree bins
|
||||||
|
edges = linspace(-pi, pi, nbins+1);
|
||||||
|
counts = histcounts(all_angles, edges, 'Normalization','probability');
|
||||||
|
|
||||||
|
% Circular variance of bond angles
|
||||||
|
R = abs(mean(exp(1i*all_angles)));
|
||||||
|
circ_variance = 1 - R; % 0 means angles clustered; 1 means uniform
|
||||||
|
|
||||||
|
% 2) Positional correlation function g(r) estimate
|
||||||
|
|
||||||
|
% Compute pairwise distances
|
||||||
|
coords = [x y];
|
||||||
|
D = pdist(coords);
|
||||||
|
max_r = min([Nx, Ny])/2; % max radius for g(r)
|
||||||
|
|
||||||
|
% Compute histogram of distances
|
||||||
|
dr = 1; % bin width in pixels (adjust)
|
||||||
|
r_edges = 0:dr:max_r;
|
||||||
|
[counts_r, r_bins] = histcounts(D, r_edges);
|
||||||
|
|
||||||
|
% Normalize g(r) by ideal gas distribution (circle annulus area)
|
||||||
|
r_centers = r_edges(1:end-1) + dr/2;
|
||||||
|
area_annulus = pi*((r_edges(2:end)).^2 - (r_edges(1:end-1)).^2);
|
||||||
|
density = Npoints / (Nx*Ny);
|
||||||
|
|
||||||
|
g_r = counts_r ./ (density * area_annulus * Npoints);
|
||||||
|
|
||||||
|
figure(1);
|
||||||
|
clf
|
||||||
|
set(gcf,'Position',[500 100 1250 500])
|
||||||
|
t = tiledlayout(1, 3, 'TileSpacing', 'compact', 'Padding', 'compact'); % 1x4 grid
|
||||||
|
|
||||||
|
nexttile
|
||||||
|
imshow(I, []);
|
||||||
|
hold on;
|
||||||
|
scatter(x, y, 30, 'w', 'filled');
|
||||||
|
hold off;
|
||||||
|
colormap(Helper.Colormaps.plasma()); % Example colormap for original image plot
|
||||||
|
cbar1 = colorbar;
|
||||||
|
cbar1.Label.Interpreter = 'latex';
|
||||||
|
xlabel('$x$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14);
|
||||||
|
ylabel('$y$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14);
|
||||||
|
title('$|\Psi(x,y)|^2$', 'Interpreter', 'latex', 'FontSize', 14);
|
||||||
|
axis square;
|
||||||
|
|
||||||
|
% --- Plot angular distribution ---
|
||||||
|
nexttile
|
||||||
|
polarhistogram(all_angles, nbins, 'Normalization', 'probability');
|
||||||
|
title(sprintf('Bond angle distribution (circ. variance = %.3f)', circ_variance));
|
||||||
|
|
||||||
|
% --- Plot g(r) ---
|
||||||
|
nexttile
|
||||||
|
plot(r_centers, g_r, 'LineWidth', 2);
|
||||||
|
xlabel('r (pixels)');
|
||||||
|
ylabel('g(r)');
|
||||||
|
title('Radial Pair Correlation Function g(r)');
|
||||||
|
grid on;
|
||||||
|
axis square
|
||||||
|
|
||||||
|
|
||||||
|
end
|
63
Data-Analyzer/execution_scripts.m
Normal file
63
Data-Analyzer/execution_scripts.m
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
%%
|
||||||
|
|
||||||
|
Data = load('D:/Results - Numerics/Data_Full3D/PhaseDiagram/ImagTimePropagation/Theta0/HighN/aS_9.562000e+01_theta_000_phi_000_N_712500/Run_000/psi_gs.mat','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;
|
||||||
|
dz = z(2)-z(1);
|
||||||
|
% Compute probability density |psi|^2
|
||||||
|
n = abs(psi).^2;
|
||||||
|
nxy = squeeze(trapz(n*dz,3));
|
||||||
|
IMG = nxy;
|
||||||
|
|
||||||
|
extractHexaticOrder(IMG)
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
Data = load('D:/Results - Numerics/Data_Full3D/PhaseDiagram/ImagTimePropagation/Theta0/HighN/aS_9.562000e+01_theta_000_phi_000_N_712500/Run_000/psi_gs.mat','psi','Params','Transf','Observ');
|
||||||
|
% Data = load('D:/Results - Numerics/Data_Full3D/PhaseDiagram/ImagTimePropagation/Theta40/HighN/aS_9.562000e+01_theta_040_phi_000_N_508333/Run_000/psi_gs.mat','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;
|
||||||
|
dz = z(2)-z(1);
|
||||||
|
% Compute probability density |psi|^2
|
||||||
|
n = abs(psi).^2;
|
||||||
|
nxy = squeeze(trapz(n*dz,3));
|
||||||
|
IMG = nxy;
|
||||||
|
|
||||||
|
%
|
||||||
|
|
||||||
|
[psi, ratio, N] = computeBondOrderParameters(IMG);
|
||||||
|
|
||||||
|
fprintf('Points: %d\n⟨|ψ₂|⟩ = %.3f, ⟨|ψ₄|⟩ = %.3f, ⟨|ψ₆|⟩ = %.3f\n', N, psi.psi2, psi.psi4, psi.psi6);
|
||||||
|
fprintf('(⟨|ψ₆|⟩ / ⟨|ψ₂|⟩) = %.3f\n', ratio);
|
98
Data-Analyzer/extractHexaticOrder.m
Normal file
98
Data-Analyzer/extractHexaticOrder.m
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
function extractHexaticOrder(I)
|
||||||
|
% Input:
|
||||||
|
% I = 2D image matrix (grayscale, double)
|
||||||
|
% Output:
|
||||||
|
% Plots original image + hexatic order + histogram
|
||||||
|
|
||||||
|
%% 1) Detect dots (local maxima)
|
||||||
|
I_smooth = imgaussfilt(I, 2);
|
||||||
|
bw = imregionalmax(I_smooth);
|
||||||
|
[y, x] = find(bw);
|
||||||
|
|
||||||
|
if length(x) < 6
|
||||||
|
error('Too few detected dots (%d) for meaningful hexatic order.', length(x));
|
||||||
|
end
|
||||||
|
|
||||||
|
%% 2) Compute hexatic order parameter psi6
|
||||||
|
|
||||||
|
% Delaunay triangulation to find neighbors
|
||||||
|
tri = delaunay(x,y);
|
||||||
|
N = length(x);
|
||||||
|
neighbors = cell(N,1);
|
||||||
|
for i = 1:size(tri,1)
|
||||||
|
neighbors{tri(i,1)} = unique([neighbors{tri(i,1)} tri(i,2) tri(i,3)]);
|
||||||
|
neighbors{tri(i,2)} = unique([neighbors{tri(i,2)} tri(i,1) tri(i,3)]);
|
||||||
|
neighbors{tri(i,3)} = unique([neighbors{tri(i,3)} tri(i,1) tri(i,2)]);
|
||||||
|
end
|
||||||
|
|
||||||
|
psi6_vals = zeros(N,1);
|
||||||
|
for j = 1:N
|
||||||
|
nbrs = neighbors{j};
|
||||||
|
nbrs(nbrs == j) = [];
|
||||||
|
if isempty(nbrs)
|
||||||
|
psi6_vals(j) = 0;
|
||||||
|
continue;
|
||||||
|
end
|
||||||
|
angles = atan2(y(nbrs)-y(j), x(nbrs)-x(j));
|
||||||
|
psi6_vals(j) = abs(mean(exp(1i*6*angles)));
|
||||||
|
end
|
||||||
|
psi6_global = mean(psi6_vals);
|
||||||
|
|
||||||
|
%% 3) Plotting
|
||||||
|
|
||||||
|
figure('Name','Hexatic Order Analysis','NumberTitle','off', 'Units','normalized', 'Position',[0.2 0.2 0.6 0.7]);
|
||||||
|
t = tiledlayout(2,2, 'Padding','none', 'TileSpacing','compact');
|
||||||
|
|
||||||
|
% Top left: Original image with detected dots (square)
|
||||||
|
ax1 = nexttile(1);
|
||||||
|
imshow(I, []);
|
||||||
|
hold on;
|
||||||
|
scatter(x, y, 30, 'w', 'filled');
|
||||||
|
hold off;
|
||||||
|
colormap(ax1, Helper.Colormaps.plasma()); % Example colormap for original image plot
|
||||||
|
cbar1 = colorbar(ax1);
|
||||||
|
cbar1.Label.Interpreter = 'latex';
|
||||||
|
xlabel(ax1, '$x$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14);
|
||||||
|
ylabel(ax1, '$y$ ($\mu$m)', 'Interpreter', 'latex', 'FontSize', 14);
|
||||||
|
title(ax1, '$|\Psi(x,y)|^2$', 'Interpreter', 'latex', 'FontSize', 14);
|
||||||
|
axis(ax1, 'square');
|
||||||
|
|
||||||
|
% Top right: Histogram of local |psi6| (square)
|
||||||
|
ax2 = nexttile(2);
|
||||||
|
histogram(psi6_vals, 20, 'FaceColor', 'b');
|
||||||
|
xlabel(ax2, '$|\psi_6|$', 'Interpreter', 'latex', 'FontSize', 14);
|
||||||
|
ylabel(ax2, 'Count');
|
||||||
|
title(ax2, 'Histogram of Local Hexatic Order');
|
||||||
|
xlim(ax2, [0 1]);
|
||||||
|
grid(ax2, 'on');
|
||||||
|
axis(ax2, 'square');
|
||||||
|
|
||||||
|
% Bottom: Hexatic order magnitude on dots with bond orientation arrows (span 2 tiles)
|
||||||
|
ax3 = nexttile([1 2]);
|
||||||
|
scatter_handle = scatter(x, y, 50, psi6_vals, 'filled');
|
||||||
|
colormap(ax3, 'jet'); % Different colormap for hexatic order magnitude
|
||||||
|
cbar3 = colorbar(ax3);
|
||||||
|
cbar3.Label.String = '|$\psi_6$|';
|
||||||
|
cbar3.Label.Interpreter = 'latex';
|
||||||
|
caxis(ax3, [0 1]);
|
||||||
|
axis(ax3, 'equal');
|
||||||
|
axis(ax3, 'tight');
|
||||||
|
title(ax3, sprintf('Local Hexatic Order |\\psi_6|, Global = %.3f', psi6_global));
|
||||||
|
hold(ax3, 'on');
|
||||||
|
|
||||||
|
for j = 1:N
|
||||||
|
nbrs = neighbors{j};
|
||||||
|
nbrs(nbrs == j) = [];
|
||||||
|
if isempty(nbrs)
|
||||||
|
continue;
|
||||||
|
end
|
||||||
|
angles = atan2(y(nbrs)-y(j), x(nbrs)-x(j));
|
||||||
|
psi6_complex = mean(exp(1i*6*angles));
|
||||||
|
local_angle = angle(psi6_complex)/6;
|
||||||
|
arrow_length = 5;
|
||||||
|
quiver(ax3, x(j), y(j), arrow_length*cos(local_angle), arrow_length*sin(local_angle), ...
|
||||||
|
'k', 'MaxHeadSize', 2, 'AutoScale', 'off');
|
||||||
|
end
|
||||||
|
hold(ax3, 'off');
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user