su2hmc
Loading...
Searching...
No Matches
clover.c
Go to the documentation of this file.
1
6#include <clover.h>
7//Multiplying by generators
8#pragma omp declare simd
9void ByGenLeft(Complex_f a[nc],const unsigned short gen){
10 Complex_f tmp = a[0];
11 switch(gen){
13 case(0):
14 a[0] = -cimagf(a[1])-crealf(a[1])*I;
15 a[1] = cimagf(tmp)+crealf(tmp)*I;
16 break;
18 case(1):
19 a[0] = -conjf(a[1]);
20 a[1] = conjf(tmp);
21 break;
23 case(2):
24 a[0] = -cimagf(a[0])+crealf(a[0])*I;
25 a[1] = -cimagf(a[1])+crealf(a[1])*I;
26 break;
27 }
28 return;
29}
30#pragma omp declare simd
31void ByGenRight(Complex_f a[nc],const unsigned short gen){
32 Complex_f tmp = a[0];
33 switch(gen){
35 case(0):
36 a[0] = -cimagf(a[1])+crealf(a[1])*I;
37 a[1] = -cimagf(tmp)+ crealf(tmp)*I;
38 break;
40 case(1):
41 a[0]=-a[1]; a[1]=tmp;
42 break;
44 case(2):
45 a[0] = -cimagf(a[0])+crealf(a[0])*I;
46 a[1] = cimagf(a[1])-crealf(a[1])*I;
47 break;
48 }
49 return;
50}
51
52//Calculating the clover and the leaves
53//=====================================
69#pragma omp declare simd
70void Half_Leaf(Complex_f Leaves[nc], Complex_f *ut[nc], Complex_f a[nc], unsigned int *iu,\
71 unsigned int *id, const unsigned int i, const unsigned short mu, const unsigned short nu, const unsigned short leaf){
72 unsigned int uidm;
73 switch(leaf){
74 case(0):
76 a[0]=ut[0][i+kvolHalo*mu]; a[1]=ut[1][i+kvolHalo*mu];
77 uidm = iu[mu*kvol+i];
78
80 Leaves[0]=a[0]*ut[0][uidm+kvolHalo*nu]-a[1]*conjf(ut[1][uidm+kvolHalo*nu]);
81 Leaves[1]=a[0]*ut[1][uidm+kvolHalo*nu]+a[1]*conjf(ut[0][uidm+kvolHalo*nu]);
82 break;
83 case(1):
85 //Should really read didm, but I've already declared this
86 uidm = id[mu*kvol+i];
87 a[0]=ut[0][i+kvolHalo*nu]; a[1]=ut[1][i+kvolHalo*nu];
88 //Awkward index...
89 const unsigned int uin_didm=iu[nu*kvol+uidm];
91 Leaves[0]=a[0]*conjf(ut[0][uin_didm+kvolHalo*mu])+a[1]*conjf(ut[1][uin_didm+kvolHalo*mu]);
92 Leaves[1]=-a[0]*ut[1][uin_didm+kvolHalo*mu]+a[1]*ut[0][uin_didm+kvolHalo*mu];
93 break;
94 case(2):
96 //Should really read didn, but I've already declared this
97 uidm = id[nu*kvol+i];
98 //Daggered. So Conj what goes into a[0] and negate what goes into a[1]
99 a[0]=conjf(ut[0][uidm+kvolHalo*nu]); a[1]=-ut[1][uidm+kvolHalo*nu];
100
102 //Don't forget negation of second term was handled earlier!
103 Leaves[0]=a[0]*ut[0][uidm+kvolHalo*mu]-a[1]*conjf(ut[1][uidm+kvolHalo*mu]);
104 Leaves[1]=a[0]*ut[1][uidm+kvolHalo*mu]+a[1]*conjf(ut[0][uidm+kvolHalo*mu]);
105 break;
106 case(3):
108 //Should really read didm, but I've already declared this
109 uidm = id[i+kvol*mu];
110 //Daggered. So Conj what goes into a[0] and negate what goes into a[1]
111 a[0]=conjf(ut[0][uidm+kvolHalo*mu]); a[1]=-ut[1][uidm+kvolHalo*mu];
112 //Another awkward index
113 const unsigned int din_didm=id[nu*kvol+uidm];
114
116 Leaves[0]=a[0]*conjf(ut[0][din_didm+kvolHalo*nu])+a[1]*conjf(ut[1][din_didm+kvolHalo*nu]);
117 Leaves[1]=-a[0]*ut[1][din_didm+kvolHalo*nu]+a[1]*ut[0][din_didm+kvolHalo*nu];
118 break;
119 }
120 return;
121}
122void Half_Leaves(Complex_f *hLeaves[2],Complex_f *ut[2], unsigned int *iu,unsigned int *id,\
123 const unsigned short mu,const unsigned short nu){
124
125#pragma omp parallel for simd collapse(2)
126 for(unsigned short leaf=0;leaf<ndim;leaf++)
127 for(unsigned int i=0;i<kvol;i++){
128 Complex_f Leaves[nc], a[nc];
129 Half_Leaf(Leaves,ut,a,iu,id,i,mu,nu,leaf);
130 hLeaves[0][i+kvol*leaf]=Leaves[0]; hLeaves[1][i+kvol*leaf]=Leaves[1];
131 }
132 return;
133}
134#pragma omp declare simd
135void Leaf(Complex_f Leaves[nc],Complex_f *ut[nc], unsigned int *iu, unsigned int *id, unsigned int i,\
136 const unsigned short mu, const unsigned short nu,const unsigned short leaf){
137 Complex_f a[nc];
138 Half_Leaf(Leaves,ut,a,iu,id,i,mu,nu,leaf);
139 unsigned int didm,didn,uidm;
140 switch(leaf){
141 case(0):
143 unsigned int uidn = iu[nu*kvol+i];
145 a[0]=Leaves[0]*conjf(ut[0][uidn+kvolHalo*mu])+Leaves[1]*conjf(ut[1][uidn+kvolHalo*mu]);
146 a[1]=-Leaves[0]*ut[1][uidn+kvolHalo*mu]+Leaves[1]*ut[0][uidn+kvolHalo*mu];
147
149 Leaves[0]=a[0]*conjf(ut[0][i+kvolHalo*nu])+a[1]*conjf(ut[1][i+kvolHalo*nu]);
150 Leaves[1]=-a[0]*ut[1][i+kvolHalo*nu]+a[1]*ut[0][i+kvolHalo*nu];
151
152 //DEBUG
153 // Leaves[0]=0; Leaves[1]=0;
154 break;
155 case(1):
157 didm = id[mu*kvol+i];
158
160 a[0]=Leaves[0]*conjf(ut[0][didm+kvolHalo*nu])+Leaves[1]*conjf(ut[1][didm+kvolHalo*nu]);
161 a[1]=-Leaves[0]*ut[1][didm+kvolHalo*nu]+Leaves[1]*ut[0][didm+kvolHalo*nu];
162
164 Leaves[0]=a[0]*ut[0][didm+kvolHalo*mu]-a[1]*conjf(ut[1][didm+kvolHalo*mu]);
165 Leaves[1]=a[0]*ut[1][didm+kvolHalo*mu]+a[1]*conjf(ut[0][didm+kvolHalo*mu]);
166 //DEBUG
167 // Leaves[0]=0; Leaves[1]=0;
168 break;
169 case(2):
171 didn = id[nu*kvol+i];
172 unsigned int uim_didn=iu[mu*kvol+didn];
174 a[0]=Leaves[0]*ut[0][uim_didn+kvolHalo*nu]-Leaves[1]*conjf(ut[1][uim_didn+kvolHalo*nu]);
175 a[1]=Leaves[0]*ut[1][uim_didn+kvolHalo*nu]+Leaves[1]*conjf(ut[0][uim_didn+kvolHalo*nu]);
176
178 Leaves[0]=a[0]*conjf(ut[0][i+kvolHalo*mu])+a[1]*conjf(ut[1][i+kvolHalo*mu]);
179 Leaves[1]=-a[0]*ut[1][i+kvolHalo*mu]+a[1]*ut[0][i+kvolHalo*mu];
180
181 //DEBUG
182 // Leaves[0]=0; Leaves[1]=0;
183 break;
184 case(3):
186 didn = id[nu*kvol+i];
187 unsigned int din_didm=id[mu*kvol+didn];
188
190 a[0]=Leaves[0]*ut[0][din_didm+kvolHalo*mu]-Leaves[1]*conjf(ut[1][din_didm+kvolHalo*mu]);
191 a[1]=Leaves[0]*ut[1][din_didm+kvolHalo*mu]+Leaves[1]*conjf(ut[0][din_didm+kvolHalo*mu]);
192
194 Leaves[0]=a[0]*ut[0][didn+kvolHalo*nu]-a[1]*conjf(ut[1][didn+kvolHalo*nu]);
195 Leaves[1]=a[0]*ut[1][didn+kvolHalo*nu]+a[1]*conjf(ut[0][didn+kvolHalo*nu]);
196
197 //DEBUG
198 // Leaves[0]=0; Leaves[1]=0;
199 break;
200 }
201 return;
202}
203void Clover(Complex_f *clover[2], Complex_f *ut[2], unsigned int *iu, unsigned int *id){
204 const char funcname[]="Full_Clover";
205#ifdef USE_GPU
206 cuClover(clover,ut,iu,id);
207#else
208 clover[0]=aligned_alloc(AVX,6*kvol*sizeof(Complex_f));
209 clover[1]=aligned_alloc(AVX,6*kvol*sizeof(Complex_f));
210 for(unsigned short mu=0;mu<ndim-1;mu++)
211 for(unsigned short nu=mu+1;nu<ndim;nu++)
212 if(mu!=nu){
213 //Clover index
214 unsigned short clov = (mu==0) ? nu-1 :mu+nu;
215#pragma omp parallel for
216 for(unsigned int i=0;i<kvol;i++){
217 clover[0][i+clov*kvol]=0;
218 clover[1][i+clov*kvol]=0;
219 Complex_f Leaves[nc];
220 for(unsigned short leaf=0;leaf<ndim;leaf++)
221 {
222 Leaf(Leaves,ut,iu,id,i,mu,nu,leaf);
223 clover[0][i+clov*kvol]+=Leaves[0]; clover[1][i+clov*kvol]+=Leaves[1];
224 }
227
232 clover[0][i+clov*kvol]=cimagf(clover[0][i+clov*kvol]); clover[0][i+clov*kvol]*=(1.0f/4.0f);
234 clover[1][i+clov*kvol]+=clover[1][i+clov*kvol]; clover[1][i+clov*kvol]*=(-I/8.0f);
235 }
236 }
237#endif
238 return;
239}
240
241//Multiplication for Congradq
242//=========================
243void ByClover(Complex *phi, Complex *r, Complex *clover[2], Complex *sigval, const float akappa, unsigned short *sigin, bool dag){
244#ifdef USE_GPU
245 cuByClover(phi,r,clover,sigval,akappa,sigin,dag);
246#else
247#pragma omp parallel for simd
248 for(unsigned int i=0;i<kvol;i++){
249 //Prefetched r and Phi array
250 Complex phi_s[ngorkov][nc];
251#pragma unroll
252 for(unsigned short igorkov=0; igorkov<ngorkov; igorkov++)
253 for(unsigned short c=0; c<nc; c++){
254 phi_s[igorkov][c]=0;
255 }
256 Complex r_s[nc];
257 Complex clov_s[nc];
258#pragma unroll
259 for(unsigned short clov=0;clov<6;clov++){
260 clov_s[0]=clover[0][clov*kvol+i]; clov_s[1]=clover[1][clov*kvol+i];
261 for(unsigned short igorkov=0; igorkov<ngorkov; igorkov++){
262 //Mod 4 done bitwise. In general n mod 2^m = n & (2^m-1)
263 const unsigned short idirac = igorkov&3;
264 const unsigned short sind = (igorkov<4) ? sigin[clov*ndirac+idirac] : sigin[clov*ndirac+idirac]+4;
265#pragma unroll
266 for(unsigned short c=0; c<nc; c++)
267 r_s[c]= r[i+kvolHalo*(sind*nc+c)];
269 phi_s[igorkov][0]+=sigval[clov*ndirac+idirac]*(creal(clov_s[0])*r_s[0]+clov_s[1]*r_s[1]);
270 //Clover is in the Lie Algebra, not Lie group. So signs are correct here.
271 phi_s[igorkov][1]+=sigval[clov*ndirac+idirac]*(conj(clov_s[1])*r_s[0]-creal(clov_s[0])*r_s[1]);
272 }
273 }
274#pragma unroll
275 for(unsigned short igorkov=0; igorkov<ngorkov; igorkov++)
276 for(unsigned short c=0; c<nc; c++){
279 //dag is just to do with the output layout and if it has a halo
280 if(dag)
281 phi[i+kvol*(nc*igorkov+c)]+=akappa*phi_s[igorkov][c];
282 else
283 phi[i+kvolHalo*(nc*igorkov+c)]+=akappa*phi_s[igorkov][c];
284 }
285 }
286#endif
287 return;
288}
289void HbyClover(Complex *phi, Complex *r, Complex *clover[2],Complex *sigval, const float akappa, unsigned short *sigin,bool dag){
290 const char funcname[] = "HbyClover";
291#ifdef USE_GPU
292 cuHbyClover(phi,r,clover,sigval,akappa,sigin,dag);
293#else
294#pragma omp parallel for simd
295 for(unsigned int i=0;i<kvol;i++){
296 //Prefetched r and Phi array
297 Complex phi_s[ndirac*nc];
298#pragma unroll
299 for(unsigned short idirac=0; idirac<ndirac*nc; idirac+=nc)
300 for(unsigned short c=0; c<nc; c++){
301 phi_s[idirac+c]=0;
302 }
303 Complex r_s[nc]; Complex clov_s[nc];
304#pragma unroll
305 for(unsigned short clov=0;clov<6;clov++){
306 clov_s[0]=clover[0][clov*kvol+i]; clov_s[1]=clover[1][clov*kvol+i];
307 for(unsigned short idirac=0; idirac<ndirac*nc; idirac+=nc){
308 const unsigned short sind = sigin[clov*ndirac+(idirac>>1)] << (nc-1);
309#pragma unroll
310 for(unsigned short c=0; c<nc; c++){
311 r_s[c]= r[i+kvolHalo*(sind+c)];
312 }
314 const Complex sig=sigval[clov*ndirac+(idirac>>1)];
315 //creal just an optimisation. Compiler can't optimise out the zero imag.
316 phi_s[idirac+0]+=sig*(creal(clov_s[0])*r_s[0]+clov_s[1]*r_s[1]);
317 //Clover is in the Lie Algebra, not Lie group. So signs are correct here.
318 phi_s[idirac+1]+=sig*(conj(clov_s[1])*r_s[0]-creal(clov_s[0])*r_s[1]);
319 }
320 }
321#pragma unroll
322 for(unsigned short idirac=0; idirac<ndirac*nc; idirac+=nc)
323 for(unsigned short c=0; c<nc; c++)
326 //dag is just to do with the output layout and if it has a halo
327 if(dag)
328 phi[i+kvol*(c+idirac)]+=akappa*phi_s[idirac+c];
329 else
330 phi[i+kvolHalo*(c+idirac)]+=akappa*phi_s[idirac+c];
331 }
332#endif
333 return;
334}
335//Float versions
336void ByClover_f(Complex_f *phi, Complex_f *r, Complex_f *clover[2], Complex_f *sigval, const float akappa, unsigned short *sigin, bool dag){
337#ifdef USE_GPU
338 cuByClover_f(phi,r,clover,sigval,akappa,sigin,dag);
339#else
340#pragma omp parallel for simd
341 for(unsigned int i=0;i<kvol;i++){
342 //Prefetched r and Phi array
343 Complex_f phi_s[ngorkov][nc];
344#pragma unroll
345 for(unsigned short igorkov=0; igorkov<ngorkov; igorkov++)
346 for(unsigned short c=0; c<nc; c++){
347 phi_s[igorkov][c]=0;
348 }
349 Complex_f r_s[nc];
350 Complex_f clov_s[nc];
351#pragma unroll
352 for(unsigned short clov=0;clov<6;clov++){
353 clov_s[0]=clover[0][clov*kvol+i]; clov_s[1]=clover[1][clov*kvol+i];
354 for(unsigned short igorkov=0; igorkov<ngorkov; igorkov++){
355 //Mod 4 done bitwise. In general n mod 2^m = n & (2^m-1)
356 const unsigned short idirac = igorkov&3;
357 const unsigned short sind = (igorkov<4) ? sigin[clov*ndirac+idirac] : sigin[clov*ndirac+idirac]+4;
358#pragma unroll
359 for(unsigned short c=0; c<nc; c++)
360 r_s[c]= r[i+kvolHalo*(sind*nc+c)];
362 phi_s[igorkov][0]+=sigval[clov*ndirac+idirac]*(crealf(clov_s[0])*r_s[0]+clov_s[1]*r_s[1]);
363 //Clover is in the Lie Algebra, not Lie group. So signs are correct here.
364 phi_s[igorkov][1]+=sigval[clov*ndirac+idirac]*(conj(clov_s[1])*r_s[0]-crealf(clov_s[0])*r_s[1]);
365 }
366 }
367#pragma unroll
368 for(unsigned short igorkov=0; igorkov<ngorkov; igorkov++)
369 for(unsigned short c=0; c<nc; c++){
372 //dag is just to do with the output layout and if it has a halo
373 if(dag)
374 phi[i+kvol*(nc*igorkov+c)]+=akappa*phi_s[igorkov][c];
375 else
376 phi[i+kvolHalo*(nc*igorkov+c)]+=akappa*phi_s[igorkov][c];
377 }
378 }
379#endif
380 return;
381}
382void HbyClover_f(Complex_f *phi, Complex_f *r, Complex_f *clover[2],Complex_f *sigval, const float akappa, unsigned short *sigin,bool dag){
383 const char funcname[] = "HbyClover_f";
384#ifdef USE_GPU
385 cuHbyClover_f(phi,r,clover,sigval,akappa,sigin,dag);
386#else
387#pragma omp parallel for simd
388 for(unsigned int i=0;i<kvol;i++){
389 //Prefetched r and Phi array
390 Complex_f phi_s[ndirac*nc];
391#pragma unroll
392 for(unsigned short idirac=0; idirac<ndirac*nc; idirac+=nc)
393 for(unsigned short c=0; c<nc; c++){
394 phi_s[idirac+c]=0;
395 }
396 Complex_f r_s[nc]; Complex_f clov_s[nc];
397#pragma unroll
398 for(unsigned short clov=0;clov<6;clov++){
399 clov_s[0]=clover[0][clov*kvol+i]; clov_s[1]=clover[1][clov*kvol+i];
400 for(unsigned short idirac=0; idirac<ndirac*nc; idirac+=nc){
401 const unsigned short sind = sigin[clov*ndirac+(idirac>>1)] << (nc-1);
402#pragma unroll
403 for(unsigned short c=0; c<nc; c++){
404 r_s[c]= r[i+kvolHalo*(sind+c)];
405 }
407 const Complex_f sig=sigval[clov*ndirac+(idirac>>1)];
408 phi_s[idirac+0]+=sig*(crealf(clov_s[0])*r_s[0]+clov_s[1]*r_s[1]);
409 //Clover is in the Lie Algebra, not Lie group. So signs are correct here.
410 phi_s[idirac+1]+=sig*(conj(clov_s[1])*r_s[0]-crealf(clov_s[0])*r_s[1]);
411 }
412 }
413#pragma unroll
414 for(unsigned short idirac=0; idirac<ndirac*nc; idirac+=nc)
415 for(unsigned short c=0; c<nc; c++)
418 //dag is just to do with the output layout and if it has a halo
419 if(dag)
420 phi[i+kvol*(c+idirac)]+=akappa*phi_s[idirac+c];
421 else
422 phi[i+kvolHalo*(c+idirac)]+=akappa*phi_s[idirac+c];
423 }
424#endif
425 return;
426}
427
428//Clover Force
429//===========
440static inline void GetBilinear(Complex_f Z[nc*nc], Bilinear_a Xmn,unsigned int ind){
441 Z[0]=Xmn.diag[ind]; Z[3]=Xmn.diag[ind+kvolHalo];
442 Z[1]=Xmn.offd[ind]; Z[2]=conjf(Z[1]);
443 return;
444}
445void CalcXmunu(Bilinear_a Xmunu, Complex_f *X1, Complex_f *X2, const Complex_f *sigval, const unsigned short *sigin,\
446 const unsigned short mu, const unsigned short nu){
447 const char funcname[] = "Xmunu";
448#ifdef USE_GPU
449 cuCalcXmunu(Xmunu,X1,X2,sigval,sigin,mu,nu);
450#else
451 unsigned short clov;
452 //Get sign and index of @f$\sigma_{\mu\nu}@f$ correct
453 clov = (mu==0) ? nu-1 : mu+nu;
454#pragma omp parallel for simd aligned(X1,X2:AVX)
455 for(unsigned int i=0;i<kvol;i++){
456 //Buffer. Four registers
457 Bilinear Xmn;
458 Xmn.diag[0]=Xmn.diag[1]=0;Xmn.offd=0;
459 for(unsigned short idirac=0; idirac<ndirac*nc; idirac+=nc){
460 const unsigned short sind = sigin[clov*ndirac+(idirac>>1)]<<1;
461 const Complex_f sig = sigval[clov*ndirac+(idirac>>1)];
462#pragma unroll
463 for(unsigned short c1=0;c1<nc;c1++){
464 //Spinors (rows) So we only load from memory once.
465 const Complex_f X1s = X1[i+kvolHalo*(sind+c1)];
466 const Complex_f X2s = X2[i+kvolHalo*(sind+c1)];
467#pragma unroll
468 for(unsigned short c2=0;c2<nc;c2++){
469 //The second off diagonal term is the conjugate of the first
470 if(c1==1&&c2==0)
471 continue;
472 //Conjugated spinor (columns).
473 const Complex_f X1c = conjf(X1[i+kvolHalo*(idirac+c2)]);
474 const Complex_f X2c = conjf(X2[i+kvolHalo*(idirac+c2)]);
475 if(c1==c2)
476 Xmn.diag[c1]+=creal(sig*(X2s*X1c+X1s*X2c));
477 else
478 Xmn.offd+=sig*(X2s*X1c+X1s*X2c);
479
480 }
481 }
482 }
483 //And write back to global memory.
484#pragma unroll
485 //Write the diagonals
486 for(unsigned short c=0;c<nc;c++)
487 Xmunu.diag[i+kvolHalo*c]=Xmn.diag[c];
488 //And the off diagonal terms
489 Xmunu.offd[i]=Xmn.offd;
490 }
491#endif
492 return;
493}
494
503static inline void GLeft(Complex_f out[4],const Complex_f G[2], const Complex_f X[4]){
504 out[0]=G[0]*X[0]+G[1]*X[2];
505 out[1]=G[0]*X[1]+G[1]*X[3];
506 out[2]=-conj(G[1])*X[0]+conj(G[0])*X[2];
507 out[3]=-conj(G[1])*X[1]+conj(G[0])*X[3];
508 return;
509}
510
518static inline void GRight(Complex_f out[4],const Complex_f G[2], const Complex_f X[4]){
519 out[0]=G[0]*X[0]-conj(G[1])*X[1];
520 out[1]=G[1]*X[0]+conj(G[0])*X[1];
521 out[2]=G[0]*X[2]-conj(G[1])*X[3];
522 out[3]=G[1]*X[2]+conj(G[0])*X[3];
523 return;
524}
525
536static inline void GSandwich(Complex_f out[4],Complex_f tmp[4], const Complex_f Gl[2], const Complex_f X[4],const Complex_f Gr[2]){
537 GRight(tmp,Gr,X);
538 GLeft(out,Gl,tmp);
539 return;
540}
541
542void Clov_Force(double *dSdpi, Complex_f *ut[2], Complex_f *X1, Complex_f *X2, const Complex_f *sigval,\
543 const unsigned short *sigin, unsigned int *iu, unsigned int *id, const float akappa){
544 const char funcname[] = "Clov_Force";
545#ifdef USE_GPU
546 cuClov_Force(dSdpi,ut,X1,X2,sigval,sigin,iu,id,akappa);
547#else
548 //Allocate the @f$X_{\mu\nu}@f$ array
549 unsigned short nclov=6; unsigned short clov=0;
550 Bilinear_a Xmn[nclov];
551 //And get the @f$X_{\mu\nu}@f$ values
552 //Loop over @f$\mu@f$ and @f$\nu@f$. Symmetry means we actually only need half the terms
553 for(unsigned short mu=0;mu<ndim-1;mu++)
554 for(unsigned short nu=mu+1;nu<ndim;nu++)
555 if(mu!=nu){
556 clov = (mu==0) ? nu-1 : mu+nu;
557 Xmn[clov].diag=(float *)aligned_alloc(AVX,2*kvolHalo*sizeof(float));
558 Xmn[clov].offd=(Complex_f *)aligned_alloc(AVX,kvolHalo*sizeof(Complex_f));
559 CalcXmunu(Xmn[clov],X1,X2,sigval,sigin,mu,nu);
560#if(nproc>1)
561 SHalo_swap_all(Xmn[clov].diag,2);
562 CHalo_swap_all(Xmn[clov].offd,1);
563#endif
564 }
565 for(unsigned short mu=0;mu<ndim;mu++)
566 for(unsigned short nu=0;nu<ndim;nu++)
567 if(mu!=nu){
568 if(mu<nu)
569 clov = (mu==0) ? nu-1 : mu+nu;
570 else
571 clov = (nu==0) ? mu-1 : mu+nu;
572#pragma omp parallel for
573 for(unsigned int i=0;i<kvol;i++){
574 //This is where it gets messy. Using HiRep/OpenQCD labelling for different intermediate values
575 //But recycling to reduce register pressure on GPU
576 //First up, W0, W1 and W6 match their Documentation values
577 Complex_f W0[2], W1[2], W6[2];
578 //Get the correct site.
579 unsigned int ind = id[i+kvol*nu];
580 //Gauge field @f$U_\nu\left(i-\hat{\nu}\right)
581 W1[0]=ut[0][ind+kvolHalo*nu]; W1[1]=ut[1][ind+kvolHalo*nu];
582
583 //@f$Z_2=X_{\mu\nu}\left(x-\hat{\nu}\right)@f$
584 Complex_f Z[nc*nc];
585 GetBilinear(Z,Xmn[clov],ind);
586
587 //W0 is @f$U^\dagger_\mu\left(x-\hat{nu}\right)@f$
588 W0[0]=conjf(ut[0][ind+kvolHalo*mu]); W0[1]=-ut[1][ind+kvolHalo*mu];
589
590 //Need a temporary Z buffers for the intermediate result
591 Complex_f Zbuff1[nc*nc], Zbuff2[nc*nc];
592 GSandwich(Zbuff1,Zbuff2,W0,Z,W1);
593
594 //@f$W_6=W_0 W_1@f$
595 W6[0]=W0[0]*W1[0]-W0[1]*conjf(W1[1]); W6[1]=W0[0]*W1[1]+W0[1]*conjf(W1[0]);
596
597 //Z_3 is the @f$X_{\mu\nu}\left(x+\hat{\mu}-\hat{\nu}\right)@f$. Store in Z
598 ind=iu[ind+kvol*mu];
599 GetBilinear(Z,Xmn[clov],ind);
600
601 //Need a second Zbuffer for another intermediate result.
602 GRight(Zbuff2,W6,Z);
603 //Sum the two results into Zbuff1. Then scale by -W5
604#pragma unroll
605 for(unsigned short c=0;c<nc*nc;c++)
606 Zbuff1[c]+=Zbuff2[c];
607 //W5 is @f$U^\dagger_\nu\left(x+\hat{\mu}-\hat{\nu}\right)@f$
608 Complex_f W5[2];
609 W5[0]=conjf(ut[0][ind+kvolHalo*nu]); W5[1]=-ut[1][ind+kvolHalo*nu];
610 //Now multiply by @f$W_5@f$ from the left into Zbuff2
611 GLeft(Zbuff2,W5,Zbuff1);
612
613 //Intermediate results from the four parts of the sum.
614 Complex_f F_int[4];
615#pragma unroll
616 for(unsigned short c=0;c<nc*nc;c++)
617 //Negative as it is @f$-W_5@f$
618 F_int[c]=-Zbuff2[c];
619
620 //Now we repeat for the last term in the sum. Recycling along the way.
621 //First store @f$W_2=U_\nu\left(x+\hat{\mu}\right)@f$ into W0.
622 ind=iu[i+kvol*mu];
623 W0[0]=ut[0][ind+kvolHalo*nu]; W0[1]=ut[1][ind+kvolHalo*nu];
624 //@f$W_3=U^\dagger_\mu\left(x+\hat{\nu}\right)@f$. Storing it in W1
625 ind=iu[i+kvol*nu];
626 W1[0]=conjf(ut[0][ind+kvolHalo*mu]); W1[1]=-ut[1][ind+kvolHalo*mu];
627 //@f$Z_4=X_{\mu\nu}\left(x+\hat{\mu}+\hat{\nu}\right)@f$. Storing in Z
628 ind=iu[ind+kvol*mu];
629 GetBilinear(Z,Xmn[clov],ind);
630 //Calculate and write into Zbuff1
631 GSandwich(Zbuff1,Zbuff2,W0,Z,W1);
632
633 //@f$W_7=W_0 W_1@f$
634 Complex_f W7[2];
635 W7[0]=W0[0]*W1[0]-W0[1]*conjf(W1[1]); W7[1]=W0[0]*W1[1]+W0[1]*conjf(W1[0]);
636 //@f$Z_5=X_{\mu\nu}\left(x+\hat{\nu}\right)@f$
637 ind=iu[i+kvol*nu];
638 GetBilinear(Z,Xmn[clov],ind);
639 //And calculate the second term
640 GLeft(Zbuff2,W7,Z);
641 //Sum the two results into Zbuff1.
642#pragma unroll
643 for(unsigned short c=0;c<nc*nc;c++)
644 Zbuff1[c]+=Zbuff2[c];
645
646 //W4 is @f$U^\dagger_\nu\left(x\right)@f$
647 Complex_f W4[2];
648 W4[0]=conjf(ut[0][i+kvolHalo*nu]); W4[1]=-ut[1][i+kvolHalo*nu];
649 //Now multiply by @f$W_4@f$ from the right into Zbuff2
650 GRight(Zbuff2,W4,Zbuff1);
651
652 //Intermediate results from the four parts of the sum.
653#pragma unroll
654 for(unsigned short c=0;c<nc*nc;c++)
655 F_int[c]+=Zbuff2[c];
656 //The last thing we need is @f$W_8=W_7W_4-W_5W_6@f$. Do it in parts and store intermediates in W0 and W1
657 W0[0]=W7[0]*W4[0]-W7[1]*conjf(W4[1]); W0[1]=W7[0]*W4[1]+W7[1]*conjf(W4[0]);
658 W1[0]=W5[0]*W6[0]-W5[1]*conjf(W6[1]); W1[1]=W5[0]*W6[1]+W5[1]*conjf(W6[0]);
659 //Store W8 in W0
660 W0[0]-=W1[0]; W0[1]-=W1[1];
661
662 //Now load @f$Z_0=X_{\mu\nu}(x)@f$
663 GetBilinear(Z,Xmn[clov],i);
664 GLeft(Zbuff1,W0,Z);
665 //And sum intermediate
666#pragma unroll
667 for(unsigned short c=0;c<nc*nc;c++)
668 F_int[c]+=Zbuff1[c];
669
670 //Now load @f$Z_1=X_{\mu\nu}\left(x+\hat{mu})@f$
671 ind=iu[i+kvol*mu];
672 GetBilinear(Z,Xmn[clov],ind);
673 GRight(Zbuff1,W0,Z);
674 //And sum intermediate
675#pragma unroll
676 for(unsigned short c=0;c<nc*nc;c++){
677 F_int[c]+=Zbuff1[c];
678 //See if this works...
679 F_int[c]*=-I;
680 }
681
682 //Excellent. Now we just need to multiply by the derivative term
683 W0[0]=ut[0][i+kvolHalo*mu]; W0[1]=ut[1][i+kvolHalo*mu];
684 for(unsigned short gen=0;gen<nadj;gen++){
685 W1[0]=W0[0]; W1[1]=W0[1];
686 ByGenLeft(W1,gen);
687 GLeft(Zbuff1,W1,F_int);
688 //Sum of the real part of the trace.
689 float dSdpis=crealf(Zbuff1[0])+crealf(Zbuff1[3]);
690 if(mu<nu)
691 dSdpi[i+kvol*(gen*ndim+mu)] -=akappa*dSdpis/4.0f;
692 else
693 dSdpi[i+kvol*(gen*ndim+mu)] +=akappa*dSdpis/4.0f;
694 }
695 }
696 }
697 for(clov=0;clov<nclov;clov++){
698 free(Xmn[clov].diag); free(Xmn[clov].offd);
699 }
700#endif
701 return;
702}
703
704//Initialisation and freeing
705int Init_clover(Complex **sigval, Complex_f **sigval_f,unsigned short **sigin, float c_sw){
706 const char funcname[] = "Init_clover";
707 unsigned short __attribute__((aligned(AVX))) sigin_t[6][4] = {{0,1,2,3},{1,0,3,2},{1,0,3,2},{1,0,3,2},{1,0,3,2},{0,1,2,3}};
708 //The sigma matrices are the commutators of the gamma matrices. These are antisymmetric when you swap the indices
709 //0 is sigma_0,1
710 //1 is sigma_0,2
711 //2 is sigma_0,3
712 //3 is sigma_1,2
713 //4 is sigma_1,3
714 //5 is sigma_2,3
715 Complex __attribute__((aligned(AVX))) sigval_t[6][4] = {{1,-1,1,-1},{I,-I,I,-I},{-1,-1,1,1},{1,1,1,1},{I,-I,-I,I},{-1,1,1,-1}};
716 //Complex __attribute__((aligned(AVX))) sigval_t[6][4] = {{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}};
717 //We mutiply by 1/2 and c_sw here since sigval is never used without them.
718#if defined USE_BLAS
719 cblas_zdscal(6*4, 0.5*c_sw, sigval_t, 1);
720#else
721#pragma omp parallel for simd collapse(2) aligned(sigval,sigval_f:AVX)
722 for(int i=0;i<6;i++)
723 for(int j=0;j<4;j++)
724 sigval_t[i][j]*=c_sw*0.5;
725#endif
726
727#ifdef USE_GPU
728 int device = -1;
729 cudaGetDevice(&device);
730
731 cudaMalloc((void **)sigin,6*4*sizeof(short));
732 cudaMalloc((void **)sigval,6*4*sizeof(Complex));
733 cudaMalloc((void **)sigval_f,6*4*sizeof(Complex_f));
734
735 cudaMemcpy(*sigin,sigin_t,6*4*sizeof(short),cudaMemcpyDefault);
736 cudaMemcpy(*sigval,sigval_t,6*4*sizeof(Complex),cudaMemcpyDefault);
737
738 cuComplex_convert(*sigval_f,*sigval,24,true,dimBlockOne,dimGridOne);
739#else
740 *sigin = (unsigned short *)malloc(6*4*sizeof(short));
741 *sigval=(Complex *)malloc(6*4*sizeof(Complex));
742 *sigval_f=(Complex_f *)malloc(6*4*sizeof(Complex_f));;
743 memcpy(*sigval,sigval_t,6*4*sizeof(Complex));
744 memcpy(*sigin,sigin_t,6*4*sizeof(short));
745 for(int i=0;i<6*4;i++)
746 *(*sigval_f+i)=(Complex_f)*(*sigval+i);
747#endif
748 return 0;
749}
750inline void Clover_free(Complex_f *clover[nc]){
751 for(unsigned short c=0;c<nc;c++){
752#ifdef USE_GPU
753#ifdef _DEBUG
754 cudaFree(clover[c]);
755#else
756 cudaFreeAsync(clover[c],streams[c]);
757#endif
758#else
759 free(clover[c]);
760#endif
761 }
762}
Routines needed for Clover improved wilson fermions.
static void GRight(Complex_f out[4], const Complex_f G[2], const Complex_f X[4])
Multiplies by a gauge field from the right.
Definition clover.c:518
void cuCalcXmunu(Bilinear_a Xmunu, Complex_f *X1, Complex_f *X2, const Complex_f *sigval, const unsigned short *sigin, const unsigned short mu, const unsigned short nu)
CUDA wrapper for CalcXmunu. Only called during testing to be honest.
Definition cuclover.cu:729
void CalcXmunu(Bilinear_a Xmunu, Complex_f *X1, Complex_f *X2, const Complex_f *sigval, const unsigned short *sigin, const unsigned short mu, const unsigned short nu)
Gets for the clover force.
Definition clover.c:445
static void GLeft(Complex_f out[4], const Complex_f G[2], const Complex_f X[4])
Multiplies by a gauge field from the left.
Definition clover.c:503
int cuClov_Force(double *dSdpi, Complex_f *ut[nc], Complex_f *X1, Complex_f *X2, const Complex_f *sigval, const unsigned short *sigin, const unsigned int *iu, const unsigned int *id, const float akappa)
CUDA wrapper for Clover_Force.
Definition cuclover.cu:736
static void GSandwich(Complex_f out[4], Complex_f tmp[4], const Complex_f Gl[2], const Complex_f X[4], const Complex_f Gr[2])
Multiplies by a gauge field from the left and the right.
Definition clover.c:536
static void GetBilinear(Complex_f Z[nc *nc], Bilinear_a Xmn, unsigned int ind)
Loads the compacted bilinear form into a complex valued matrix.
Definition clover.c:440
void Clov_Force(double *dSdpi, Complex_f *ut[2], Complex_f *X1, Complex_f *X2, const Complex_f *sigval, const unsigned short *sigin, unsigned int *iu, unsigned int *id, const float akappa)
Gets the clover contribution to the force.
Definition clover.c:542
void cuHbyClover(Complex *phi, Complex *r, Complex *clover[nc], Complex *sigval, const float akappa, unsigned short *sigin, bool dag)
CUDA wrapper for HbyClover.
Definition cuclover.cu:719
void cuByClover_f(Complex_f *phi, Complex_f *r, Complex_f *clover[nc], Complex_f *sigval, const float akappa, unsigned short *sigin, bool dag)
CUDA wrapper for ByClover_f.
Definition cuclover.cu:722
void cuHbyClover_f(Complex_f *phi, Complex_f *r, Complex_f *clover[nc], Complex_f *sigval, const float akappa, unsigned short *sigin, bool dag)
CUDA wrapper for HbyClover_f.
Definition cuclover.cu:725
void HbyClover(Complex *phi, Complex *r, Complex *clover[2], Complex *sigval, const float akappa, unsigned short *sigin, bool dag)
Clover analogue of the Dslash operation. This version acts on all flavours similar to Dslash and Dsla...
Definition clover.c:289
void ByClover(Complex *phi, Complex *r, Complex *clover[2], Complex *sigval, const float akappa, unsigned short *sigin, bool dag)
Clover analogue of the Dslash operation. This version acts on all flavours similar to Dslash and Dsla...
Definition clover.c:243
void cuByClover(Complex *phi, Complex *r, Complex *clover[nc], Complex *sigval, const float akappa, unsigned short *sigin, bool dag)
CUDA wrapper for ByClover.
Definition cuclover.cu:716
void ByClover_f(Complex_f *phi, Complex_f *r, Complex_f *clover[2], Complex_f *sigval, const float akappa, unsigned short *sigin, bool dag)
Clover analogue of the Dslash operation. This version acts on all flavours similar to Dslash and Dsla...
Definition clover.c:336
void HbyClover_f(Complex_f *phi, Complex_f *r, Complex_f *clover[2], Complex_f *sigval, const float akappa, unsigned short *sigin, bool dag)
Clover analogue of the Dslash operation. This version acts on all flavours similar to Dslash and Dsla...
Definition clover.c:382
int cuClover(Complex_f *clover[nc], Complex_f *ut[nc], unsigned int *iu, unsigned int *id)
CUDA wrapper for calculating the clovers in all directions at all sites .
Definition cuclover.cu:694
void ByGenRight(Complex_f a[nc], const unsigned short gen)
Multiply leaf (or part of one) by generator from right.
Definition clover.c:31
void Half_Leaf(Complex_f Leaves[nc], Complex_f *ut[nc], Complex_f a[nc], unsigned int *iu, unsigned int *id, const unsigned int i, const unsigned short mu, const unsigned short nu, const unsigned short leaf)
Calculates the first half of the leaf for a clover term. We split it so that the force term can reuse...
Definition clover.c:70
void Clover_free(Complex_f *clover[nc])
Free's memory used for clover terms and leaves.
Definition clover.c:750
void Leaf(Complex_f Leaves[nc], Complex_f *ut[nc], unsigned int *iu, unsigned int *id, unsigned int i, const unsigned short mu, const unsigned short nu, const unsigned short leaf)
Calculates a leaf for a clover term.
Definition clover.c:135
void Half_Leaves(Complex_f *hLeaves[2], Complex_f *ut[2], unsigned int *iu, unsigned int *id, const unsigned short mu, const unsigned short nu)
Calculates the products of the first two links in a plaquette.
Definition clover.c:122
int Init_clover(Complex **sigval, Complex_f **sigval_f, unsigned short **sigin, float c_sw)
Initialise values needed for the clover terms.
Definition clover.c:705
void ByGenLeft(Complex_f a[nc], const unsigned short gen)
Multiply leaf (or part of one) by generator from left.
Definition clover.c:9
void Clover(Complex_f *clover[2], Complex_f *ut[2], unsigned int *iu, unsigned int *id)
Calculates the clovers in all directions at all sites.
Definition clover.c:203
__device__ __forceinline__ T conj(const T &z)
Complex Conjugation.
Definition cusu2hmc.cu:33
void cuComplex_convert(Complex_f *a, Complex *b, const unsigned int len, const bool dtof, dim3 dimBlock, dim3 dimGrid)
takes an array of complex float and double precision numbers and converts the precision
Definition cusu2hmc.cu:284
int CHalo_swap_all(Complex_f *c, int ncpt)
Calls the functions to send data to both the up and down halos.
int SHalo_swap_all(float *d, int ncpt)
Calls the functions to send data to both the up and down halos.
#define AVX
Alignment of arrays. 64 for AVX-512, 32 for AVX/AVX2. 16 for SSE. Since AVX is standard on modern x86...
Definition sizes.h:279
#define nc
Colours.
Definition sizes.h:182
#define ngorkov
Gor'kov indices.
Definition sizes.h:190
#define nadj
adjacent spatial indices
Definition sizes.h:184
#define kvol
Sublattice volume.
Definition sizes.h:163
#define Complex
Double precision complex number.
Definition sizes.h:64
#define ndirac
Dirac indices.
Definition sizes.h:186
dim3 dimBlockOne
block size of one
Definition cusu2hmc.cu:22
dim3 dimGridOne
Grid size of one.
Definition cusu2hmc.cu:23
#define Complex_f
Single precision complex number.
Definition sizes.h:62
#define ndim
Dimensions.
Definition sizes.h:188
#define kvolHalo
Subvolume + halo size.
Definition sizes.h:234
Structure of arrays for Hermitian bilinear in memory.
Definition clover.h:30
Complex_f * offd
Complex valued off-diagonal terms. We only need to store one of these to get the other in .
Definition clover.h:34
float * diag
Real valued diagonal terms.
Definition clover.h:32
Hermitian bilinear on the local stack.
Definition clover.h:40
float diag[2]
Real valued diagonal terms.
Definition clover.h:42
Complex_f offd
Complex valued off-diagonal terms. We only need to store one of these to get the other in .
Definition clover.h:44
cudaStream_t streams[ndirac *ndim *nadj]
An array of concurrent GPU streams to keep it busy.
Definition cusu2hmc.cu:29
#define creal(z)
Extract Real Component using C standard notation.
#define I
Define I in double precision using C standard notation.