forked from ilent2/ott
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdda_vaterite.m
213 lines (168 loc) · 6.64 KB
/
dda_vaterite.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
% Generate a T-matrix for a vaterite particle using DDA
%
% This example shows how DDA can be used to simulate a vaterite
% particle either as a homogeneous spherical particle or with a
% sheaf-of-wheat structure.
%
% The example calculates the T-matrix, calculates the torque when
% the particle is placed in a Gaussian beam and shows runtime information.
%
% This script takes a couple of minutes on a i7 with 16 GB ram.
% If this is too slow on your system, try reducing the number
% of vaterites and the vaterite size. If you run out of memory,
% try the slower low memory DDA (see comment bellow).
%
% This simulation uses a rather large spacing (lambda/10) and is likely
% very inacurate, a spacing of lambda/20 or smaller would be preferable.
%
% Copyright 2020 Isaac Lenton.
% This file is part of the optical tweezers toolbox.
% See LICENSE.md for information about using/distributing this file.
% Add OTT to the path (only required if OTT isn't already on the path)
% This assumes this script is in the ott/examples directory, if you
% moved the script you may need to modify this command
addpath('../');
% Make warnings less obtrusive
ott.warning('once');
ott.change_warnings('off');
%% Define properties for the particle and beam
% Refractive indices of water and vaterite
% Vaterite is positive uniaxial (n_e > n_o)
index_medium = 1.33;
index_o = 1.55;
index_e = 1.65;
% Vacuum wavelength [m]
wavelength0 = 1064e-9;
% Medium and particle wavelengths [m]
wavelength_medium = wavelength0 ./ index_medium;
wavelength_particle = wavelength0 ./ [index_o, index_e];
% Describe the particle properties
spacing = min(wavelength_particle) / 10;
%% Visualise the sheaf-of-wheat structur
%
% In this example we model the alignment of the vaterite crystal with
% a sheaf-of-wheat structure. The definition is at the end of this file.
%
% The following code generates a image of the structure.
sc = 1e-6;
tol = 1e-16; % Shift focus slightly away from zero to supress warning
x = linspace(-1, 1, 100)*sc;
y = linspace(-1, 1, 100)*sc;
[xx, yy] = meshgrid(x, y);
r = [0*xx(:), xx(:), yy(:)].';
v = sheafOfWheat(r+tol, 0.5*sc);
small = 1.0e-4*sc;
figure();
streamline(xx, yy, ...
reshape(v(2, :), size(xx)), reshape(v(3, :), size(xx)), ...
[linspace(-1, 1, 100), linspace(-1, 1, 100)]*sc, [zeros(1, 100)+small, zeros(1, 100)-small]);
axis image
title('Sheaf-of-wheat visualisation');
xlabel('X Position [m]');
ylabel('Z Position [m]');
%% Generate the T-matrix
%
% To calculates the T-matrix we use DDA with mirror symmetry and
% rotational symmetry optimisations.
% DDA has a low memory implementation but it is slower
% Enable this if you run out of ram for the larger particles
low_memory = false;
% The method supports using '\' and 'gmres'. Set use iterative to
% true to use 'gmres' for solving the DDA system. (Might be faster).
use_iterative = false;
radius = logspace(-8, -6.2147, 20);
% radius = 1.0e-6; % Try with low_memory
% radius = logspace(-8, -6, 20);
times = zeros(size(radius));
% Setup variable for T-matrix (ensure it is free)
Ts = ott.TmatrixDda.empty(1, 0);
for ii = 1:length(times)
% Pre-calculate voxel locations
shape = ott.shapes.Sphere(radius(ii));
voxels = shape.voxels(spacing, 'even_range', true);
if numel(voxels) == 0
times(ii) = nan;
continue;
end
% Calculate polarizability unit
upol = ott.utils.polarizability.LDR(spacing ./ wavelength0, [index_o; index_o; index_e] ./ index_medium);
% Calculate sheaf-of-wheta orientations
dirs = sheafOfWheat(voxels, 0.5*radius(ii));
% Rotate polarizability and index_relative units
% We need index_relative to ensure correct scaling of the TE modes
index_relative = ott.utils.rotate_3x3tensor(...
diag([index_o, index_o, index_e] ./ index_medium), 'dir', dirs);
polarizabilities = ott.utils.rotate_3x3tensor(...
diag(upol), 'dir', dirs);
tic();
Tmatrix = ott.TmatrixDda(voxels, ...
'polarizability', polarizabilities, ...
'index_medium', index_medium, ...
'spacing', spacing, ...
'z_rotational_symmetry', 0, ...
'z_mirror_symmetry', true, ...
'wavelength0', wavelength0, ...
'low_memory', low_memory, ...
'use_iterative', use_iterative);
times(ii) = toc();
Ts(end+1) = Tmatrix;
end
%% Generate plot of runtime
figure();
loglog(radius, times, '*--');
xlabel('Radius [m]');
ylabel('Time [s]');
title('DDA Run-time');
%% Generate plot of the torque
%
% This is probably not very acurate and not very large or tiny particles.
%
% If we do larger particles and improve the accuracy we should be able
% to approximate figure 3 from
%
% Highly birefringent vaterite microspheres: production,
% characterization and applications for optical micromanipulation.
% S. Parkin, et al., Optics Express Vol. 17, Issue 24, pp. 21944-21955 (2009)
% https://doi.org/10.1364/OE.17.021944
%
% Tips to try to reproduce this figure: specify the ms to use with
% TmatrixDda to match the ms in the beam. Increase resolution.
beam = ott.BscPmGauss('NA', 1.1, 'index_medium', index_medium, ...
'power', 1.0, 'wavelength0', wavelength0, 'polarisation', [1, -1i]);
[~, tz] = ott.forcetorque(beam, Ts, ...
'position', [0;0;0], 'rotation', eye(3));
figure();
plot(radius(end-length(Ts)+1:end), tz(3:3:end, :));
xlabel('Radius [m]');
ylabel('Torque [a.u.]');
%% Sheaf-of-wheat definition
function v = sheafOfWheat(voxels, foci_distance)
% Generate the vectors for the sheaf-of-wheat structure
%
% - voxels (3xN numeric) -- voxel locations
% - foci_distance (numeric) -- distance from centre to focii
%
% Based on code from Vincent Loke (circa 2009)
if any(voxels(:) == 0)
warning('Voxels should not be placed on planes through 0');
end
xneg = voxels(1, :) < 0;
yneg = voxels(2, :) < 0;
zneg = voxels(3, :) < 0;
voxels = abs(voxels);
rho = sqrt(voxels(1, :).^2 + voxels(2, :).^2);
z = voxels(3, :);
gam = asin((2^(1/2)*((foci_distance^4 - 2*foci_distance^2*rho.^2 ...
+ 2*foci_distance^2*z.^2 + rho.^4 + 2*rho.^2.*z.^2 + z.^4).^(1/2) ...
+ foci_distance^2 - rho.^2 - z.^2).^(1/2))/(2*foci_distance));
dzdrho = foci_distance/2*sin(gam).* ...
((rho./(foci_distance*cos(gam))).^2-1).^(-.5).* ...
(2*rho)./(foci_distance*cos(gam)).^2;
theta = pi/2 - atan(dzdrho); % theta is relative to the z-axis
rho = sin(theta);
phi = atan(voxels(2,:)./voxels(1,:));
v = [rho.*cos(phi); rho.*sin(phi); cos(theta)];
v(1, xneg) = -v(1, xneg);
v(2, yneg) = -v(2, yneg);
v(3, zneg) = -v(3, zneg);
end