GPUE  v1.0
GPU Gross-Pitaevskii Equation numerical solver for Bose-Einstein condensates
tracker.cu
Go to the documentation of this file.
1 
2 #include "../include/tracker.h"
3 #include "../include/fileIO.h"
4 #include "../include/minions.h"
5 #include "../include/constants.h"
6 #include "../include/vort.h"
7 
8 /**
9  * Contains all the glorious info you need to track vortices and see what they are up to.
10  **/
11 namespace Tracker {
12  char bufferT[1024];
13 
14  /**
15  * Determines the vortex separation at the centre of the lattice.
16  */
17  double vortSepAvg(const std::vector<std::shared_ptr<Vtx::Vortex> > &vArray, const std::shared_ptr<Vtx::Vortex> centre){
18  double min = 0.0;
19  double min_tmp = 0.0;
20  min = sqrt( pow(centre->getCoordsD().x - vArray[0]->getCoordsD().x,2) + pow(centre->getCoordsD().y - vArray[0]->getCoordsD().y,2));
21  for (int j=1; j<vArray.size(); ++j){
22  min_tmp = sqrt( pow(centre->getCoordsD().x - vArray[j]->getCoordsD().x,2) + pow(centre->getCoordsD().y - vArray[j]->getCoordsD().y,2));
23  if(min > min_tmp && min_tmp > 1e-7){//100nm length
24  min = min_tmp;
25  }
26  }
27  return min;
28  }
29 
30  /**
31  * Finds the maxima of the optical lattice. Deprecated.
32  */
33  [[deprecated]]
34  int findOLMaxima(int *marker, double *Vopt, double radius, int xDim, double* x){
35  double gridValues[9];
36  int2 mIndex[1024];
37  int2 index;
38  int i,j,found;
39  found=0;
40  for (i=1; i<xDim-1; ++i ){
41  for(j=1; j<xDim-1;++j){
42  if(sqrt(x[i]*x[i] + x[j]*x[j]) < radius){
43  gridValues[0] = Vopt[(i-1)*xDim + (j-1)];
44  gridValues[1] = Vopt[(i-1)*xDim + j];
45  gridValues[2] = Vopt[(i-1)*xDim + (j+1)];
46  gridValues[3] = Vopt[i*xDim + (j-1)];
47  gridValues[4] = Vopt[i*xDim + j];
48  gridValues[5] = Vopt[i*xDim + (j+1)];
49  gridValues[6] = Vopt[(i+1)*xDim + (j-1)];
50  gridValues[7] = Vopt[(i+1)*xDim + j];
51  gridValues[8] = Vopt[(i+1)*xDim + (j+1)];
52  if(fabs((gridValues[4]-Minions::maxValue(gridValues,9))/gridValues[4]) <= 1e-7){
53  //printf ("%d,%d\n",i,j);
54  (marker)[i*xDim + j] = 1;
55  index.x=i;
56  index.y=j;
57  mIndex[found] = index;
58  ++found;
59  }
60  }
61  }
62  }
63  return found;
64  }
65 
66  #ifdef VORT_MIN
67  [[deprecated]]
68  int findVortex(int *marker, const double2* wfc, double radius, int xDim,
69  const double* x, int timestep){
70 
71  double gridValues[9];
72  int2 vIndex[1024];
73  int2 index;
74  int i,j,found;
75  found=0;
76  // #pragma omp parallel for private(j)
77  for (i=1; i<xDim-1; ++i ){
78  for(j=1; j<xDim-1;++j){
79  if(sqrt(x[i]*x[i] + x[j]*x[j]) < radius){
80  gridValues[0] = Minions::psi2(wfc[(i-1)*xDim + (j-1)]);
81  gridValues[1] = Minions::psi2(wfc[(i-1)*xDim + j]);
82  gridValues[2] = Minions::psi2(wfc[(i-1)*xDim + (j+1)]);
83  gridValues[3] = Minions::psi2(wfc[(i)*xDim + (j-1)]);
84  gridValues[4] = Minions::psi2(wfc[(i)*xDim + j]);
85  gridValues[5] = Minions::psi2(wfc[(i)*xDim + (j+1)]);
86  gridValues[6] = Minions::psi2(wfc[(i+1)*xDim + (j-1)]);
87  gridValues[7] = Minions::psi2(wfc[(i+1)*xDim + j]);
88  gridValues[8] = Minions::psi2(wfc[(i+1)*xDim + (j+1)]);
89  if(fabs((gridValues[4]-Minions::minValue(gridValues,9)) /
90  gridValues[4]) < 1e-7){
91  //printf ("%d,%d\n",i,j);
92  (marker)[i*xDim + j] = 1;
93  index.x=i;
94  index.y=j;
95  vIndex[found] = index;
96  found++;
97  }
98  }
99  }
100  }
101  return found;
102  }
103  #else
104  /**
105  * Phase winding method to determine vortex positions. Calculates the phase around a loop and checks if ~ +/-2Pi.
106  */
107  int findVortex(int *marker, const double2* wfc, double radius, int xDim, const double *x, int timestep){
108  double2 *g = (double2*) malloc(sizeof(double2)*4);
109  double *phiDelta = (double*) malloc(sizeof(double)*4);
110  int i,j,found;
111  int cond_x, cond_y;
112  cond_x = 0; cond_y = 0;
113  found = 0;
114  long rnd_value = 0;
115  double sum = 0.0;
116  for ( i=0; i < xDim-1; ++i ){
117  for( j=0; j < xDim-1; ++j ){
118  if(sqrt(x[i]*x[i] + x[j]*x[j]) < radius){
119  g[0] = Minions::complexScale(
120  Minions::complexDiv( wfc[i*xDim + j],
121  wfc[(i+1)*xDim + j] ),
122  (Minions::complexMag(wfc[(i+1)*xDim+j])
123  / Minions::complexMag(wfc[i*xDim+j])));
124  g[1] = Minions::complexScale(
125  Minions::complexDiv(wfc[(i+1)*xDim+j],
126  wfc[(i+1)*xDim + (j+1)] ),
127  (Minions::complexMag(wfc[(i+1)*xDim+(j+1)])
128  / Minions::complexMag( wfc[(i+1)*xDim + j] )));
129  g[2] = Minions::complexScale(
130  Minions::complexDiv( wfc[(i+1)*xDim + (j+1)],
131  wfc[i*xDim + (j+1)] ),
132  ( Minions::complexMag( wfc[i*xDim + (j+1)])
133  / Minions::complexMag( wfc[(i+1)*xDim + (j+1)] )));
134  g[3] = Minions::complexScale(
135  Minions::complexDiv( wfc[i*xDim + (j+1)],
136  wfc[i*xDim + j] ),
137  ( Minions::complexMag( wfc[i*xDim + j])
138  / Minions::complexMag( wfc[i*xDim + (j+1)] )));
139  for (int k=0; k<4; ++k){
140  phiDelta[k] = atan2( g[k].y, g[k].x );
141  if(phiDelta[k] <= -PI){
142  phiDelta[k] += 2*PI;
143  }
144  }
145  sum = phiDelta[0] + phiDelta[1] + phiDelta[2] + phiDelta[3];
146  rnd_value = lround(sum/(2*PI));
147  if( sum >= 1.9*PI && cond_x <= 0 && cond_y <= 0){
148  marker[i*xDim + j] = rnd_value;
149  ++found;
150  sum = 0.0;
151  cond_x = 2; cond_y = 2;
152  }
153  else if( sum <= -1.9*PI && cond_x <= 0 && cond_y <= 0 ) {
154  marker[i*xDim + j] = -rnd_value;
155  ++found;
156  sum = 0.0;
157  cond_x = 2; cond_y = 2;
158  }
159  --cond_x;
160  --cond_y;
161  }
162  }
163  }
164  free(g); free(phiDelta);
165  return found;
166  }
167  #endif
168 
169  /**
170  * Accepts matrix of vortex locations as argument, returns array of x,y coordinates of locations and first encountered vortex angle
171  */
172  [[deprecated]]
173  void olPos(int *marker, int2 *olLocation, int xDim){
174  int i,j;
175  unsigned int counter=0;
176  for(i=0; i<xDim; ++i){
177  for(j=0; j<xDim; ++j){
178  if((marker)[i*xDim + j] == 1){
179  (olLocation)[ counter ].x=i;
180  (olLocation)[ counter ].y=j;
181  ++counter;
182  }
183  }
184  }
185  }
186 
187  /**
188  * Tests the phase winding of the wavefunction, looking for vortices
189  */
190  int phaseTest(int2 vLoc, const double2* wfc, int xDim){
191  int result = 0;
192  double2 gridValues[4];
193  double phiDelta[4];
194  double sum=0.0;
195  int i=vLoc.x, j=vLoc.y;
196  gridValues[0] = Minions::complexScale( Minions::complexDiv(wfc[i*xDim + j],wfc[(i+1)*xDim + j]), (Minions::complexMag(wfc[(i+1)*xDim + j]) / Minions::complexMag(wfc[i*xDim + j])));
197  gridValues[1] = Minions::complexScale( Minions::complexDiv(wfc[(i+1)*xDim + j],wfc[(i+1)*xDim + (j+1)]), (Minions::complexMag(wfc[(i+1)*xDim + (j+1)])/ Minions::complexMag(wfc[(i+1)*xDim + j])));
198  gridValues[2] = Minions::complexScale( Minions::complexDiv(wfc[(i+1)*xDim + (j+1)],wfc[i*xDim + (j+1)]), (Minions::complexMag(wfc[i*xDim + (j+1)]) / Minions::complexMag(wfc[(i+1)*xDim + (j+1)])));
199  gridValues[3] = Minions::complexScale( Minions::complexDiv(wfc[i*xDim + (j+1)],wfc[i*xDim + j]), (Minions::complexMag(wfc[i*xDim + j]) / Minions::complexMag(wfc[i*xDim + (j+1)])));
200 
201  for (int k=0; k<4; ++k){
202  phiDelta[k] = atan2(gridValues[k].y,gridValues[k].x);
203  if(phiDelta[k] <= -PI){
204  phiDelta[k] += 2*PI;
205  }
206  }
207  sum = phiDelta[0] + phiDelta[1] + phiDelta[2] + phiDelta[3];
208  if(sum >=1.8*PI){
209  result = 1;
210  }
211  return result;
212  }
213 
214  /**
215  * Accepts matrix of vortex locations as argument, returns array of x,y coordinates of locations and first encountered vortex angle
216  */
217  void vortPos(const int *marker, std::vector<std::shared_ptr<Vtx::Vortex> > &vLocation, int xDim, const double2 *wfc){
218  int i,j;
219  unsigned int counter=0;
220 
221  int2 coords; double2 coordsD;
222  coords.x=0; coords.y = 0;
223  coordsD.x=0.; coordsD.y = 0.;
224 
225  for( i = 0; i < xDim; ++i){
226  for( j = 0; j < xDim; ++j){
227  if( abs((marker)[i*xDim + j]) >= 1){
228  coords.x = i; coords.y = j;
229  vLocation.push_back(std::make_shared<Vtx::Vortex>(coords, coordsD, marker[i*xDim + j], false, 0));
230  //++counter;
231  }
232  }
233  }
234  }
235 
236  /**
237  * Ensures the vortices are tracked and arranged in the right order based on minimum distance between previous and current positions
238  */
239  void vortArrange(std::vector<std::shared_ptr<Vtx::Vortex> > &vCoordsC, const std::vector<std::shared_ptr<Vtx::Vortex> > &vCoordsP){
240  int dist, dist_t;
241  int i, j, index;
242  for ( i = 0; i < vCoordsC.size(); ++i ){
243  dist = 0x7FFFFFFF; //arbitrary big value fo initial distance value
244  index = i;
245  for ( j = i; j < vCoordsC.size() ; ++j){//Changed to C and P from num_vort[0] size for both. May be an issue here with inconsistent sizing
246  dist_t = ( (vCoordsP[i]->getCoordsD().x
247  - vCoordsC[j]->getCoordsD().x)
248  * (vCoordsP[i]->getCoordsD().x
249  - vCoordsC[j]->getCoordsD().x)
250  + (vCoordsP[i]->getCoordsD().y
251  - vCoordsC[j]->getCoordsD().y)
252  * (vCoordsP[i]->getCoordsD().y
253  - vCoordsC[j]->getCoordsD().y) );
254  if(dist > dist_t ){
255  dist = dist_t;
256  index = j;
257  }
258  }
259  std::swap(vCoordsC[index], vCoordsC[i]); // Swap the elements at the given positions. Remove call to Minions::coordSwap(vCoordsC,index,i);
260  }
261  }
262 
263  /**
264  * Determines the coords of the vortex closest to the central position. Useful for centering the optical lattice over v. lattice*
265  */
266  std::shared_ptr<Vtx::Vortex> vortCentre(const std::vector<std::shared_ptr<Vtx::Vortex> > &cArray, int xDim){
267  int i, counter=0;
268  int valX, valY;
269  double valueTest, value = 0.0;
270  valX = (cArray)[0]->getCoordsD().x - ((xDim/2)-1);
271  valY = (cArray)[0]->getCoordsD().y - ((xDim/2)-1);
272  value = sqrt( valX*valX + valY*valY );//Calcs the sqrt(x^2+y^2) from central position. try to minimise this value
273  for ( i=1; i<cArray.size(); ++i ){
274  valX = (cArray)[i]->getCoordsD().x - ((xDim/2)-1);
275  valY = (cArray)[i]->getCoordsD().y - ((xDim/2)-1);
276  valueTest = sqrt(valX*valX + valY*valY);
277  if(value > valueTest){
278  value = valueTest;
279  counter = i;
280  }
281  }
282  return (cArray)[counter];
283  }
284 
285  /**
286  * Determines the angle of the vortex lattice relative to the x-axis
287  */
288  double vortAngle(const std::vector<std::shared_ptr<Vtx::Vortex>> &vortCoords, const std::shared_ptr<Vtx::Vortex> central){
289  int location = 0;
290  double minVal=1e300;//(pow(central.x - vortCoords[0].x,2) + pow(central.y - vortCoords[0].y,2));
291  for (int i=0; i < vortCoords.size(); ++i){//Assuming healing length on the order of 2 um
292  if (minVal > (pow(central->getCoordsD().x - vortCoords[i]->getCoordsD().x,2) + pow(central->getCoordsD().y - vortCoords[i]->getCoordsD().y,2)) && std::abs(central->getCoordsD().x - vortCoords[i]->getCoordsD().x) > 2e-6 && std::abs(central->getCoordsD().y - vortCoords[i]->getCoordsD().y) > 2e-6){
293  minVal = (pow(central->getCoordsD().x - vortCoords[i]->getCoordsD().x,2) + pow(central->getCoordsD().y - vortCoords[i]->getCoordsD().y,2));
294  location = i;
295  }
296  }
297  double ang=(fmod(atan2( (vortCoords[location]->getCoordsD().y - central->getCoordsD().y), (vortCoords[location]->getCoordsD().x - central->getCoordsD().x) ),PI/3));
298  printf("Angle=%e\n",ang);
299  return PI/3 - ang;
300 
301  //return PI/2 + fmod(atan2(vortCoords[location].y-central.y, vortCoords[location].x - central.x), PI/3);
302  //return PI/2 - sign*acos( ( (central.x - vortCoords[location].x)*(central.x - vortCoords[location].x) ) / ( minVal*(central.x - vortCoords[location].x) ) );
303  }
304 
305  /**
306  * Sigma of vortex lattice and optical lattice. Deprecated
307  */
308  [[deprecated]]
309  double sigVOL(const std::vector<std::shared_ptr<Vtx::Vortex> > &vArr, const int2 *opLatt, const double *x){
310  double sigma = 0.0;
311  double dx = std::abs(x[1]-x[0]);
312  for (int i=0; i<vArr.size(); ++i){
313  sigma += pow( std::abs( sqrt( (vArr[i]->getCoordsD().x - opLatt[i].x)*(vArr[i]->getCoordsD().x - opLatt[i].x) + (vArr[i]->getCoordsD().y - opLatt[i].y)*(vArr[i]->getCoordsD().y - opLatt[i].y) )*dx),2);
314  }
315  sigma /= vArr.size();
316  return sigma;
317  }
318 
319  /**
320  * Performs least squares fitting to get exact vortex core position.
321  */
322  void lsFit(std::vector<std::shared_ptr<Vtx::Vortex> > &vortCoords, const double2 *wfc, int xDim){
323  double2 *wfc_grid = (double2*) malloc(sizeof(double2)*4);
324  double2 *res = (double2*) malloc(sizeof(double2)*3);
325  double2 R;
326  double2 coordsAdjusted;
327  double det=0.0;
328  for(int ii=0; ii < vortCoords.size(); ++ii){
329  //vortCoords[ii]->getCoordsD().x = 0.0; vortCoords[ii]->getCoordsD().y = 0.0;
330  coordsAdjusted.x=0.; coordsAdjusted.y=0.;
331 
332  wfc_grid[0] = wfc[vortCoords[ii]->getCoords().x*xDim + vortCoords[ii]->getCoords().y];
333  wfc_grid[1] = wfc[(vortCoords[ii]->getCoords().x + 1)*xDim + vortCoords[ii]->getCoords().y];
334  wfc_grid[2] = wfc[vortCoords[ii]->getCoords().x*xDim + (vortCoords[ii]->getCoords().y + 1)];
335  wfc_grid[3] = wfc[(vortCoords[ii]->getCoords().x + 1)*xDim + (vortCoords[ii]->getCoords().y + 1)];
336 
337  for(int jj=0; jj<3; ++jj) {
338  res[jj].x = lsq[jj][0]*wfc_grid[0].x + lsq[jj][1]*wfc_grid[1].x + lsq[jj][2]*wfc_grid[2].x + lsq[jj][3]*wfc_grid[3].x;
339  res[jj].y = lsq[jj][0]*wfc_grid[0].y + lsq[jj][1]*wfc_grid[1].y + lsq[jj][2]*wfc_grid[2].y + lsq[jj][3]*wfc_grid[3].y;
340  }
341 
342  //Solve Ax=b here. A = res[0,1], b = - res[2]. Solution -> X
343  det = 1.0/(res[0].x*res[1].y - res[0].y*res[1].x);
344  R.x = det*(res[1].y*res[2].x - res[0].y*res[2].y);
345  R.y = det*(-res[1].x*res[2].x + res[0].x*res[2].y);
346  coordsAdjusted.x = vortCoords[ii]->getCoords().x - R.x;
347  coordsAdjusted.y = vortCoords[ii]->getCoords().y - R.y;
348  vortCoords[ii]->updateCoordsD(coordsAdjusted);
349  }
350  }
351 
352  void updateVortices(std::shared_ptr<Vtx::VtxList> vLCurrent, std::shared_ptr<Vtx::VtxList> &vLPrev){
353  //Iterate through the previous vortices, and compare the distances
354  for (auto vtxPrev : vLPrev->getVortices()) {
355  auto vtxMin = vLCurrent->minDistPair(vtxPrev, 1);
356  //If the vortex has a min-pairing, and has a UID < 0 then we can assign it the same UID as the paired vortex
357  if (vtxMin.second != nullptr && vtxMin.second->getUID() < 0 ) {
358  vtxMin.second->updateUID(vtxPrev->getUID());
359  vtxMin.second->updateIsOn(true);
360  }
361  //If no pairing found, then the vortex has disappeared or been killed. Switch it off, and add it to the current list with the given UID
362  else{
363  vtxPrev->updateIsOn(false);
364  vLCurrent->addVtx(vtxPrev);//Will this cause trouble? Maybe rethink the UID determination
365  }
366  }
367  //Find new vortices, assign them UIDs and switch them on
368  for (auto v: vLCurrent->getVortices()) {
369  if (v->getUID() < 0){
370  v->updateUID(vLCurrent->getMax_Uid()++);
371  v->updateIsOn(true);
372  }
373  }
374  //Sort the list based on vortex UIDS. This may not be necessary, but helps for now with debugging things
375  std::sort(
376  vLCurrent->getVortices().begin(),
377  vLCurrent->getVortices().end(),
378  []( std::shared_ptr<Vtx::Vortex> a,
379  std::shared_ptr<Vtx::Vortex> b) {
380  return b->getUID() < a->getUID();
381  });
382  //Overwrite previous list with current list.
383  vLPrev->getVortices().swap(vLCurrent->getVortices());
384  }
385 }