@@ -159,6 +159,11 @@ double skyrmion_number(double *spin, double *charge,
159159}
160160
161161double dot (double * a , double * b ){
162+ /* Dot product for vectors of 3 components, given by arrays a and b. If
163+ * pointers are passed, it uses the first three components starting from
164+ * that place in memory, i.e. if we pass &a[2], where a = {0, 1, 2, 3, 4},
165+ * the dot product uses {2, 3, 4} as a vector (same for b)
166+ */
162167 double dp = 0 ;
163168 int i ;
164169
@@ -167,6 +172,35 @@ double dot(double *a, double *b){
167172}
168173
169174double compute_BergLuscher_angle (double * s1 , double * s2 , double * s3 ){
175+
176+ /* Compute the spherical angle given by the neighbouring spin 3-vectors s1,
177+ * s2 and s3 (these are arrays that can be also passed as pointers; see the
178+ * -dot- function). The spherical angle Omega, defined by the
179+ * vectors in a unit sphere, is computed using the imaginary exponential
180+ * defined by Berg and Luscher [Nucl Phys B 190, 412 (1981)]:
181+
182+ * exp (i sigma Omega) = 1 + s1 * s2 + s2 * s3 + s3 * s1 + i s1 * (s2 X s3)
183+ * ------------------------------------------------
184+ * 2 (1 + s1 * s2) (1 + s2 * s3) (1 + s3 * s1)
185+
186+ * The denominator is a normalisation factor which we call rho, and i
187+ * stands for an imaginary number in the numerator. The factor sigma is an
188+ * orientation given by sign(s1 * s2 X s3), however, we do not use it since
189+ * we take counter clock wise directions for the (s1, s2, s3) triangle of
190+ * spins, as pointed out by Yin et al. [PRB 93, 174403 (2016)].
191+ *
192+ * Therefore we use a complex logarithm:
193+
194+ * clog( r * exp(i theta) ) = r + i theta
195+
196+ * to calculate the angle Omega, since the clog is well defined in the
197+ * [-PI, PI] range, giving the correct sign for the topological number (we
198+ * could also use the arcsin when decomposing the exp).
199+ *
200+ * Notice we normalise the angle by a 4 PI factor
201+ *
202+ */
203+
170204 double rho ;
171205 double complex exp ;
172206 double crossp [3 ];
@@ -180,8 +214,8 @@ double compute_BergLuscher_angle(double *s1, double *s2, double *s3){
180214 * (1 + dot (& s3 [0 ], & s1 [0 ]))
181215 );
182216
183- exp = (1 + dot (& s1 [0 ], & s2 [0 ])
184- + dot (& s2 [0 ], & s3 [0 ])
217+ exp = (1 + dot (& s1 [0 ], & s2 [0 ])
218+ + dot (& s2 [0 ], & s3 [0 ])
185219 + dot (& s3 [0 ], & s1 [0 ])
186220 + I * dot (& s1 [0 ], & crossp [0 ])
187221 ) / rho ;
@@ -193,23 +227,84 @@ double compute_BergLuscher_angle(double *s1, double *s2, double *s3){
193227double skyrmion_number_BergLuscher (double * spin , double * charge ,
194228 int nx , int ny , int nz , int * ngbs ) {
195229
196- int n = nx * ny * nz ;
197- int i , spin_index ;
198- double total_sum = 0 ;
230+ /* Compute the topological charge (or skyrmion number) by adding triangles
231+ * of neighbouring spins for every lattice site, which cover triangle areas
232+ * in a unit sphere (i.e. we map the lattice area into a unit sphere
233+ * surface, using a triangulation). The exponential that defines every
234+ * spherical angle was firstly mentioned by Berg and Luscher [Nucl Phys B
235+ * 190, 412 (1981)] for a discrete square lattice but we generalise it here
236+ * for hexagonal crystals.
237+ *
238+ * NEIGHBOURS DEFINITION:
239+ *
240+ * *ngbs is the array with the neighbours information for every lattice
241+ * site. For cuboid meshes, the array is like:
242+ *
243+ * NN: j =0 j=1 ... j=0 j=1 ...
244+ * [ 0-x, 0+x, 0-y, 0+y, 0-z, 0+z, 1-x, 1+x, 1-y, ... ]
245+ * spin: i=6 * 0 i=6 * 1
246+ * ...
247+ *
248+ * where 0-y is the index of the neighbour of the 0th spin, in the -y
249+ * direction, for example, so the neighbours of the i-th spin start at the
250+ * (6 * i) position of the array. This is similar for hexagonal meshes.
251+ *
252+ * For a cuboid mesh, for the i-th spin, we generate the nearest ngbs
253+ * triangles using the triangles: [i, j=1, j=3] , [i, j=0, j=2]
254+ * +x +y -x -y
255+ * i.e. we cover the top right and bottom left areas ( we could also use
256+ * the top left and bottom right)
257+ *
258+ * For a hexagonal mesh, for the i-th spin, we generate the nearest ngbs
259+ * triangles using the triangles: [i, j=0, j=2] , [i, j=0, j=2]
260+ * E NE W SW
261+ * (East) ...
262+ * whose area covers a unit cell in a hexagonal crystal arrangement.
263+ *
264+ *
265+ * Since the neighbours agree in indexes we can use the same function
266+ * for both meshes.
267+ *
268+ * ------------------------------------------------------------------------
269+ *
270+ * Having the triangles defined, we compute the spherical triangle area
271+ * spanned by the three spins using the compute_BergLuscher_angle function.
272+ *
273+ * The total topological charge Q is computed summing all triangles:
274+ * __
275+ * Q = \ 1 [ Omega(S_i, S_0, S_2) + Omega(S_i, S_1, S_3) ]
276+ * /__ ---
277+ * 4 PI
278+ * i
279+ *
280+ * ehich are normalised by 4 PI, so we get an integer number for the number
281+ * of times the unit sphere is covered by the spin directions in every
282+ * triangle, e.g. if we have two skyrmions we get approximately Q = 2.
283+ *
284+ */
285+
286+ int n = nx * ny * nz ; int i , spin_index ; double total_sum = 0 ;
199287
288+ // Sweep through every lattice site
200289 for (i = 0 ; i < n ; i ++ ){
201290
291+ // x-y-z components for the i-th spin start at:
202292 spin_index = 3 * i ;
203293
294+ // Reset the charge array
204295 charge [i ] = 0 ;
205296
297+ // Compute the spherical triangle area for the triangle formed
298+ // by the i-th spin and neighbours 0 and 2, i.e.
299+ // [i j=0 j=2]. First check that the NNs exist:
206300 if (ngbs [6 * i ] > 0 && ngbs [6 * i + 2 ] > 0 ){
207301 charge [i ] += compute_BergLuscher_angle (& spin [spin_index ],
208302 & spin [3 * ngbs [6 * i ]],
209303 & spin [3 * ngbs [6 * i + 2 ]]
210304 );
211305 }
212306
307+ // Triangle: [i j=1 j=3]
213308 if (ngbs [6 * i + 1 ] > 0 && ngbs [6 * i + 3 ] > 0 ){
214309 charge [i ] += compute_BergLuscher_angle (& spin [spin_index ],
215310 & spin [3 * ngbs [6 * i + 1 ]],
0 commit comments