11#include "clib.h"
2+ #include "math.h"
3+ #include "complex.h"
24
35//compute the S \cdot (S_i \times S_j)
46inline double volume (double S [3 ], double Si [3 ], double Sj [3 ]) {
@@ -17,7 +19,7 @@ double skyrmion_number(double *spin, double *charge,
1719 *
1820 * The *spin array is the vector field for a two dimensional
1921 * lattice with dimensions nx * ny
20- * (we can take a slice of a bulk from Python and pass it here,
22+ * (we can take a slice of a bulk from Python and pass it here,
2123 * remember to do the ame for the neighbours matrix)
2224 * The array follows the order:
2325 * [Sx0 Sy0 Sz0 Sx1 Sy1 Sz1 ... ]
@@ -50,17 +52,17 @@ double skyrmion_number(double *spin, double *charge,
5052 *
5153 * Then, we use the follwing expression:
5254 *
53- * Q = S_i \dot ( S_{i+1} \times S_{j+1} )
55+ * Q = S_i \dot ( S_{i+1} \times S_{j+1} )
5456 * + S_i \dot ( S_{i-1} \times S_{j-1} )
5557 *
5658 * This expression is based on the publication PRL 108, 017601 (2012)
5759 * where Q is called "finite spin chirality". The idea comes from
5860 * discrete chiral quantities in Hall effect studies. For example, at
5961 * the end of page 3 in Rep. Prog. Phys. 78 (2015) 052502, it
6062 * is argued:
61- * scalar chirality (...) , which measures the volume enclosed
63+ * scalar chirality (...) , which measures the volume enclosed
6264 * by the three spins of the elementary triangle and, similarly to
63- * (the vector chirlity) is sensitive to the sense of spin's
65+ * (the vector chirlity) is sensitive to the sense of spin's
6466 * rotation in the x–y plane
6567 *
6668 * Hence we are taking the triangles formed by (i, i+1, j+1)
@@ -72,12 +74,12 @@ double skyrmion_number(double *spin, double *charge,
7274 *
7375 * Recently, other ways to calculate a discrete skyrmion number have
7476 * been proposed: http://arxiv.org/pdf/1601.08212.pdf
75- * Phys. Rev. B 93, 024417
76- *
77+ * Phys. Rev. B 93, 024417
78+ *
7779 * also based on using three spins using triangles. This could be
78- * useful for applying to a hexagonal lattice in the future.
80+ * useful for applying to a hexagonal lattice in the future.
7981 *
80- */
82+ */
8183
8284 int i , j ;
8385 int index , id ;
@@ -91,7 +93,7 @@ double skyrmion_number(double *spin, double *charge,
9193 for (i = 0 ; i < nxy ; i ++ ) {
9294 index = 3 * i ;
9395
94- /* The starting index of the nearest neighbours for the
96+ /* The starting index of the nearest neighbours for the
9597 * i-th spin */
9698 int id_nn = 6 * i ;
9799
@@ -156,6 +158,166 @@ double skyrmion_number(double *spin, double *charge,
156158
157159}
158160
161+ double 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+ */
167+ double dp = 0 ;
168+ int i ;
169+
170+ for (i = 0 ; i < 3 ; i ++ ) dp += a [i ] * b [i ];
171+ return dp ;
172+ }
173+
174+ double 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+
204+ double rho ;
205+ double complex exp ;
206+ double crossp [3 ];
207+
208+ crossp [0 ] = s2 [1 ] * s3 [2 ] - s2 [2 ] * s3 [1 ];
209+ crossp [1 ] = s2 [2 ] * s3 [0 ] - s2 [0 ] * s3 [2 ];
210+ crossp [2 ] = s2 [0 ] * s3 [1 ] - s2 [1 ] * s3 [0 ];
211+
212+ rho = sqrt (2 * (1 + dot (& s1 [0 ], & s2 [0 ]))
213+ * (1 + dot (& s2 [0 ], & s3 [0 ]))
214+ * (1 + dot (& s3 [0 ], & s1 [0 ]))
215+ );
216+
217+ exp = (1 + dot (& s1 [0 ], & s2 [0 ])
218+ + dot (& s2 [0 ], & s3 [0 ])
219+ + dot (& s3 [0 ], & s1 [0 ])
220+ + I * dot (& s1 [0 ], & crossp [0 ])
221+ ) / rho ;
222+
223+ return 2 * cimagl (clog (exp )) / (4 * WIDE_PI );
224+
225+ }
226+
227+ double skyrmion_number_BergLuscher (double * spin , double * charge ,
228+ int nx , int ny , int nz , int * ngbs ) {
229+
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 ;
287+
288+ // Sweep through every lattice site
289+ for (i = 0 ; i < n ; i ++ ){
290+
291+ // x-y-z components for the i-th spin start at:
292+ spin_index = 3 * i ;
293+
294+ // Reset the charge array
295+ charge [i ] = 0 ;
296+
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:
300+ if (ngbs [6 * i ] > 0 && ngbs [6 * i + 2 ] > 0 ){
301+ charge [i ] += compute_BergLuscher_angle (& spin [spin_index ],
302+ & spin [3 * ngbs [6 * i ]],
303+ & spin [3 * ngbs [6 * i + 2 ]]
304+ );
305+ }
306+
307+ // Triangle: [i j=1 j=3]
308+ if (ngbs [6 * i + 1 ] > 0 && ngbs [6 * i + 3 ] > 0 ){
309+ charge [i ] += compute_BergLuscher_angle (& spin [spin_index ],
310+ & spin [3 * ngbs [6 * i + 1 ]],
311+ & spin [3 * ngbs [6 * i + 3 ]]
312+ );
313+ }
314+
315+ total_sum += charge [i ];
316+ }
317+
318+ return total_sum ;
319+ }
320+
159321//compute the first derivative respect to x and for the whole mesh
160322//assume 2d pbc is used
161323void compute_px_py_c (double * spin , int nx , int ny , int nz , double * px , double * py ){
0 commit comments