su2hmc
Loading...
Searching...
No Matches
cuclover.cu
Go to the documentation of this file.
1
8#include <clover.h>
9
10//CUDA Device code
11namespace Device{
23 template <typename T>
24 __device__ void cuByGenLeft(T a[nc],const unsigned short gen){
25 T tmp = a[0];
26 switch(gen){
28 case(0):
29 a[0] = T(-a[1].imag(), -a[1].real());
30 a[1] = T( tmp.imag(), tmp.real());
31 break;
33 case(1):
34 a[0] = -conj(a[1]);
35 a[1] = conj(tmp);
36 break;
38 case(2):
39 a[0] = T(-a[0].imag(), a[0].real());
40 a[1] = T(-a[1].imag(), a[1].real());
41 break;
42 }
43 return;
44 }
45
55 template <typename T>
56 __device__ void cuByGenRight(T a[nc],const unsigned short gen){
57 T tmp = a[0];
58 switch(gen){
60 case(0):
61 a[0] = T(-a[1].imag(), a[1].real());
62 a[1] = T(-tmp.imag(), tmp.real());
63 break;
65 case(1):
66 a[0]=-a[1]; a[1]=tmp;
67 break;
69 case(2):
70 a[0] = T(-a[0].imag(), a[0].real());
71 a[1] = T( a[1].imag(), -a[1].real());
72 break;
73 }
74 return;
75 }
76
90 template <typename T>
91 __device__ void Half_Leaf(complex<T> Leaves[nc], complex<T> *u11t, complex<T> *u12t, complex<T> a[nc], unsigned int *iu,\
92 unsigned int *id, const unsigned int i, const unsigned short mu, const unsigned short nu, const unsigned short leaf){
93
94 unsigned int ind; unsigned int double_ind=id[nu*kvol+ind];
95 switch(leaf){
96 case(0):
98 a[0]=u11t[i+kvolHalo*mu]; a[1]=u12t[i+kvolHalo*mu];
99 ind = iu[mu*kvol+i];
100
102 Leaves[0]=a[0]*u11t[ind+kvolHalo*nu]-a[1]*conj(u12t[ind+kvolHalo*nu]);
103 Leaves[1]=a[0]*u12t[ind+kvolHalo*nu]+a[1]*conj(u11t[ind+kvolHalo*nu]);
104 break;
105 case(1):
107 ind = id[mu*kvol+i];
108 a[0]=u11t[i+kvolHalo*nu]; a[1]=u12t[i+kvolHalo*nu];
109 //Awkward index...
110 double_ind=iu[nu*kvol+ind];
112 Leaves[0]=a[0]*conj(u11t[double_ind+kvolHalo*mu])+a[1]*conj(u12t[double_ind+kvolHalo*mu]);
113 Leaves[1]=-a[0]*u12t[double_ind+kvolHalo*mu]+a[1]*u11t[double_ind+kvolHalo*mu];
114 break;
115 case(2):
117 ind = id[nu*kvol+i];
118 //Daggered. So Conj what goes into a[0] and negate what goes into a[1]
119 a[0]=conj(u11t[ind+kvolHalo*nu]); a[1]=-u12t[ind+kvolHalo*nu];
120
122 Leaves[0]=a[0]*u11t[ind+kvolHalo*mu]-a[1]*conj(u12t[ind+kvolHalo*mu]);
123 //Don't forget negatiion of second term was handled earlier!
124 Leaves[1]=a[0]*u12t[ind+kvolHalo*mu]+a[1]*conj(u11t[ind+kvolHalo*mu]);
125 break;
126 case(3):
128 ind = id[i+kvol*mu];
129 //Daggered. So Conj what goes into a[0] and negate what goes into a[1]
130 a[0]=conj(u11t[ind+kvolHalo*mu]); a[1]=-u12t[ind+kvolHalo*mu];
131 //Another awkward index
132 double_ind=id[nu*kvol+ind];
133
135 Leaves[0]=a[0]*conj(u11t[double_ind+kvolHalo*nu])+a[1]*conj(u12t[double_ind+kvolHalo*nu]);
136 Leaves[1]=-a[0]*u12t[double_ind+kvolHalo*nu]+a[1]*u11t[double_ind+kvolHalo*nu];
137 break;
138 }
139 return;
140 }
141
152 template <typename T>
153 __device__ void Leaf(complex<T> *u11t, complex<T> *u12t, complex<T> Leaves[nc],\
154 unsigned int *iu, unsigned int *id, unsigned int i,const unsigned short mu,\
155 const unsigned short nu,const unsigned short leaf){
156 complex<T> a[nc];
157 Half_Leaf(Leaves,u11t,u12t,a,iu,id,i,mu,nu,leaf);
158 unsigned int ind,double_ind;
159 switch(leaf){
160 case(0)://Need braces for strict C++ standard compliance as a variable is declared in the case
161 {
162 ind = iu[nu*kvol+i];
164 a[0]=Leaves[0]*conj(u11t[ind+kvolHalo*mu])+Leaves[1]*conj(u12t[ind+kvolHalo*mu]);
165 a[1]=-Leaves[0]*u12t[ind+kvolHalo*mu]+Leaves[1]*u11t[ind+kvolHalo*mu];
166
168 Leaves[0]=a[0]*conj(u11t[i+kvolHalo*nu])+a[1]*conj(u12t[i+kvolHalo*nu]);
169 Leaves[1]=-a[0]*u12t[i+kvolHalo*nu]+a[1]*u11t[i+kvolHalo*nu];
170
171 }
172 break;
173 case(1):
175 ind = id[mu*kvol+i];
176
178 a[0]=Leaves[0]*conj(u11t[ind+kvolHalo*nu])+Leaves[1]*conj(u12t[ind+kvolHalo*nu]);
179 a[1]=-Leaves[0]*u12t[ind+kvolHalo*nu]+Leaves[1]*u11t[ind+kvolHalo*nu];
180
182 Leaves[0]=a[0]*u11t[ind+kvolHalo*mu]-a[1]*conj(u12t[ind+kvolHalo*mu]);
183 Leaves[1]=a[0]*u12t[ind+kvolHalo*mu]+a[1]*conj(u11t[ind+kvolHalo*mu]);
184 break;
185 case(2):
187 ind = id[nu*kvol+i];
188 double_ind=iu[mu*kvol+ind];
190 a[0]=Leaves[0]*u11t[double_ind+kvolHalo*nu]-Leaves[1]*conj(u12t[double_ind+kvolHalo*nu]);
191 a[1]=Leaves[0]*u12t[double_ind+kvolHalo*nu]+Leaves[1]*conj(u11t[double_ind+kvolHalo*nu]);
192
194 Leaves[0]=a[0]*conj(u11t[i+kvolHalo*mu])+a[1]*conj(u12t[i+kvolHalo*mu]);
195 Leaves[1]=-a[0]*u12t[i+kvolHalo*mu]+a[1]*u11t[i+kvolHalo*mu];
196
197 break;
198 case(3)://Need braces for strict C++ standard compliance as a variable is declared in the case
200 ind = id[nu*kvol+i];
201 double_ind=id[mu*kvol+ind];
202
204 a[0]=Leaves[0]*u11t[double_ind+kvolHalo*mu]-Leaves[1]*conj(u12t[double_ind+kvolHalo*mu]);
205 a[1]=Leaves[0]*u12t[double_ind+kvolHalo*mu]+Leaves[1]*conj(u11t[double_ind+kvolHalo*mu]);
206
208 Leaves[0]=a[0]*u11t[ind+kvolHalo*nu]-a[1]*conj(u12t[ind+kvolHalo*nu]);
209 Leaves[1]=a[0]*u12t[ind+kvolHalo*nu]+a[1]*conj(u11t[ind+kvolHalo*nu]);
210 break;
211 }
212 return;
213 }
214
223 template <typename T>
224 __device__ void cuGLeft(T out[4],const T G[2], const T X[4]){
225 out[0]=G[0]*X[0]+G[1]*X[2];
226 out[1]=G[0]*X[1]+G[1]*X[3];
227 out[2]=-conj(G[1])*X[0]+conj(G[0])*X[2];
228 out[3]=-conj(G[1])*X[1]+conj(G[0])*X[3];
229 return;
230 }
231
239 template <typename T>
240 __device__ void cuGRight(T out[4],const T G[2], const T X[4]){
241 out[0]=G[0]*X[0]-conj(G[1])*X[1];
242 out[1]=G[1]*X[0]+conj(G[0])*X[1];
243 out[2]=G[0]*X[2]-conj(G[1])*X[3];
244 out[3]=G[1]*X[2]+conj(G[0])*X[3];
245 return;
246 }
247
256 template <typename T>
257 __device__ void cuGSandwich(T out[4],T tmp[4], const T Gl[2], const T X[4],const T Gr[2]){
258 cuGRight(tmp,Gr,X);
259 cuGLeft(out,Gl,tmp);
260 return;
261 }
262
272 __device__ void GetBilinear(Complex_f Z[nc*nc], Bilinear_a Xmn,unsigned int ind){
273 Z[0]=Xmn.diag[ind]; Z[3]=Xmn.diag[ind+kvolHalo];
274 Z[1]=Xmn.offd[ind]; Z[2]=conj(Z[1]);
275 return;
276 }
277}
278
280namespace Kernels{
281 using namespace Device;
293 template <typename T>
294 __global__ void Half_Leaves(complex<T> *hLeaves0,complex<T> *hLeaves1,complex<T> *u11t,complex<T> *u12t,\
295 unsigned int *iu,unsigned int *id,const unsigned short mu,const unsigned short nu){
296 const volatile int gsize = gridDim.x*gridDim.y*gridDim.z;
297 const volatile int bsize = blockDim.x*blockDim.y*blockDim.z;
298 const volatile int blockId = blockIdx.x+ blockIdx.y * gridDim.x+ gridDim.x * gridDim.y * blockIdx.z;
299 const volatile int bthreadId= (threadIdx.z * blockDim.y+ threadIdx.y)* blockDim.x+ threadIdx.x;
300 const volatile int gthreadId= blockId * bsize+bthreadId;
301
302 complex<T> Leaves[nc], a[nc];
303 for(unsigned short leaf=0;leaf<ndim;leaf++)
304 for(unsigned int i=gthreadId;i<kvol;i+=gsize*bsize){
305 Half_Leaf(Leaves,u11t,u12t,a,iu,id,i,mu,nu,leaf);
306 hLeaves0[i+kvol*leaf]=Leaves[0]; hLeaves1[i+kvol*leaf]=Leaves[1];
307 }
308 return;
309 }
310
322 template <typename T>
323 __global__ void Full_Clover(complex<T> *clover1, complex<T> *clover2,\
324 complex<T> *u11t, complex<T> *u12t, unsigned int *iu, unsigned int *id, int mu, int nu){
325 const volatile int gsize = gridDim.x*gridDim.y*gridDim.z;
326 const volatile int bsize = blockDim.x*blockDim.y*blockDim.z;
327 const volatile int blockId = blockIdx.x+ blockIdx.y * gridDim.x+ gridDim.x * gridDim.y * blockIdx.z;
328 const volatile int bthreadId= (threadIdx.z * blockDim.y+ threadIdx.y)* blockDim.x+ threadIdx.x;
329 const volatile int gthreadId= blockId * bsize+bthreadId;
330
331 complex<T> Leaves[2];
332 for(unsigned int i=gthreadId;i<kvol;i+=gsize*bsize){
333 clover1[i]=0;clover2[i]=0;
334 for(unsigned short leaf=0;leaf<ndim;leaf++)
335 {
336 //Pointer arithemetic on the leaves.
337 Leaf(u11t,u12t,Leaves,iu,id,i,mu,nu,leaf);
338 clover1[i]+=Leaves[0]; clover2[i]+=Leaves[1];
339 }
342
346 clover1[i]=clover1[i].imag(); clover1[i]*=(1.0f/4.0f);
347 // clover1[i]=clover1[1].imag()/4.0f;
348
350 clover2[i]+=clover2[i]; clover2[i]*=(-I_f/8.0f);
351 }
352 return;
353 }
354
368 template <typename T>
369 __global__ void cuCalcXmunu(Bilinear_a Xmunu, const complex<T> *X1, const complex<T> *X2,
370 const complex<T> *sigval, const unsigned short *sigin,const unsigned short clov){
371 const char funcname[] = "Xmunu";
372 const unsigned int gsize = gridDim.x*gridDim.y*gridDim.z;
373 const unsigned int bsize = blockDim.x*blockDim.y*blockDim.z;
374 const unsigned int blockId = blockIdx.x+ blockIdx.y * gridDim.x+ gridDim.x * gridDim.y * blockIdx.z;
375 const unsigned int bthreadId= (threadIdx.z * blockDim.y+ threadIdx.y)* blockDim.x+ threadIdx.x;
376 const unsigned int gthreadId= blockId * bsize+bthreadId;
377 //Get sign and index of @f$\sigma_{\mu\nu}@f correct
378 for(unsigned int i=gthreadId;i<kvol;i+=gsize*bsize){
379 //Buffer. Four registers
380 Bilinear Xmn;
381 Xmn.diag[0]=Xmn.diag[1]=0;Xmn.offd=0;
382 for(unsigned short idirac=0; idirac<ndirac*nc; idirac+=nc){
383 const unsigned short sind = sigin[clov*ndirac+(idirac>>1)]<<1;
384 const complex<T> sig = sigval[clov*ndirac+(idirac>>1)];
385 for(unsigned short c1=0;c1<nc;c1++){
386 //Spinors (rows) So we only load from memory once.
387 const complex<T> X1s = X1[i+kvolHalo*(sind+c1)];
388 const complex<T> X2s = X2[i+kvolHalo*(sind+c1)];
389 for(unsigned short c2=0;c2<nc;c2++){
390 //The second off diagonal term is the conjugate of the first
391 if(c1==1&&c2==0)
392 continue;
393 //Conjugated spinor (columns).
394 const complex<T> X1c = conj(X1[i+kvolHalo*(idirac+c2)]);
395 const complex<T> X2c = conj(X2[i+kvolHalo*(idirac+c2)]);
396 if(c1==c2)
397 Xmn.diag[c1]+=(T)(sig*(X2s*X1c+X1s*X2c)).real();
398 else
399 Xmn.offd+=sig*(X2s*X1c+X1s*X2c);
400 }
401 }
402 }
403 //Write the diagonals
404 for(unsigned short c=0;c<nc;c++)
405 Xmunu.diag[i+kvolHalo*c]=Xmn.diag[c];
406 //And the off diagonal terms
407 Xmunu.offd[i]=Xmn.offd;
408 }
409 return;
410 }
411
426 template <typename T>
427 __global__ void Clov_Force(double *dSdpi, const complex<T> *u11t, const complex<T> *u12t, Bilinear_a Xmn,\
428 const complex<T> *sigval, const unsigned short *sigin, const unsigned int *iu,\
429 const unsigned int *id, const float akappa,const unsigned short mu, const unsigned short nu){
430 const unsigned int gsize = gridDim.x*gridDim.y*gridDim.z;
431 const unsigned int bsize = blockDim.x*blockDim.y*blockDim.z;
432 const unsigned int blockId = blockIdx.x+ blockIdx.y * gridDim.x+ gridDim.x * gridDim.y * blockIdx.z;
433 const unsigned int bthreadId= (threadIdx.z * blockDim.y+ threadIdx.y)* blockDim.x+ threadIdx.x;
434 const unsigned int gthreadId= blockId * bsize+bthreadId;
435 //And get the @f$X_{\mu\nu}@f$ values
436 //Loop over @f$\mu@f$ and @f$\nu@f$,
437 for(unsigned int i=gthreadId;i<kvol;i+=gsize*bsize){
438 //Buffer for intermediate force calculation. One for each generator.
439 //This is where it gets messy. Using HiRep/OpenQCD labelling for different intermediate values
440 //But recycling to reduce register pressure on GPU
441 //First up, W0, W1 and W6 match their Documentation values
442 complex<T> W0[2]; complex<T> W1[2]; complex<T> W6[2];
443 //Get the correct site. Originally ind and stood for up and down. Then I realised only one was needed
444 //at a time and am too lazy to change it everywhere.
445 unsigned int ind = id[i+kvol*nu];
446 //Gauge field @f$U_\nu\left(i-\hat{\nu}\right)
447 W1[0]=u11t[ind+kvolHalo*nu]; W1[1]=u12t[ind+kvolHalo*nu];
448
449 //@f$Z_2=X_{\mu\nu}\left(i-\hat{\nu}\right)@f$
450 complex<T> Z[nc*nc];
451 Device::GetBilinear(Z,Xmn,ind);
452
453 //W0 is @f$U^\dagger_\mu@f(x-\hat{nu}\right)@f$
454 W0[0]=conj(u11t[ind+kvolHalo*mu]); W0[1]=-u12t[ind+kvolHalo*mu];
455
456 //Need a temporary Z buffers for the intermediate result
457 complex<T> Zbuff1[nc*nc];complex<T> Zbuff2[nc*nc];
458 Device::cuGSandwich(Zbuff1,Zbuff2,W0,Z,W1);
459
460 //@f$W_6=W_0 W_1@f$
461 W6[0]=W0[0]*W1[0]-W0[1]*conj(W1[1]); W6[1]=W0[0]*W1[1]+W0[1]*conj(W1[0]);
462
463 //Z3 is the @f$X_{\mu\nu}\left(x+\hat{\mu}-\hat{\nu}\right)@f$. Store in Z
464 ind=iu[ind+kvol*mu];
465 Device::GetBilinear(Z,Xmn,ind);
466
467 //Need a second Zbuffer for another intermediate result.
468 Device::cuGRight(Zbuff2,W6,Z);
469 //Sum the two results into Zbuff1. Then scale by -W5
470#pragma unroll
471 for(unsigned short c=0;c<nc*nc;c++)
472 Zbuff1[c]+=Zbuff2[c];
473 //W5 is @f$U^\dagger_\nu\left(x+\hat{\mu}-\hat{\nu}\right)@f$
474 complex<T> W5[2];
475 W5[0]=conj(u11t[ind+kvolHalo*nu]); W5[1]=-u12t[ind+kvolHalo*nu];
476 //Now multiply by @f$W_5@f$ from the left into Zbuff2
477 Device::cuGLeft(Zbuff2,W5,Zbuff1);
478
479 //Intermediate results from the four parts of the sum.
480 complex<T> F_int[4];
481#pragma unroll
482 for(unsigned short c=0;c<nc*nc;c++)
483 //Negative as it is @f$-W_5@f$
484 F_int[c]=-Zbuff2[c];
485
486 //Now we repeat for the last term in the sum. Recycling along the way.
487 //First store @f$W_2=U_\nu\left(x+\hat{\mu}\right)@f$ into W0.
488 ind=iu[i+kvol*mu];
489 W0[0]=u11t[ind+kvolHalo*nu]; W0[1]=u12t[ind+kvolHalo*nu];
490 //@f$W_3=U^\dagger_\mu\left(x+\hat{\nu}\right). Storing it in W1
491 ind=iu[i+kvol*nu];
492 W1[0]=conj(u11t[ind+kvolHalo*mu]); W1[1]=-u12t[ind+kvolHalo*mu];
493 //@f$Z_4=X_{\mu\nu}\left(x+\hat{\mu}+\hat{\nu}\right)@f$. Storing in Z
494 ind=iu[ind+kvol*mu];
495 Device::GetBilinear(Z,Xmn,ind);
496 //Calculate and write into Zbuff1
497 Device::cuGSandwich(Zbuff1,Zbuff2,W0,Z,W1);
498
499 //@f$W_7=W_0 W_1@f$
500 complex<T> W7[2];
501 W7[0]=W0[0]*W1[0]-W0[1]*conj(W1[1]); W7[1]=W0[0]*W1[1]+W0[1]*conj(W1[0]);
502 //@f$Z_5=X_{\mu\nu}\left(x+\hat{\nu}\right)@f$
503 ind=iu[i+kvol*nu];
504 Device::GetBilinear(Z,Xmn,ind);
505 //And calculate the second term
506 Device::cuGLeft(Zbuff2,W7,Z);
507 //Sum the two results into Zbuff1.
508#pragma unroll
509 for(unsigned short c=0;c<nc*nc;c++)
510 Zbuff1[c]+=Zbuff2[c];
511 //W4 is @f$U^\dagger_\nu\left(x\right)@f$
512 complex<T> W4[2];
513 W4[0]=conj(u11t[i+kvolHalo*nu]); W4[1]=-u12t[i+kvolHalo*nu];
514 //Now multiply by @f$W_4@f$ from the right into Zbuff2
515 Device::cuGRight(Zbuff2,W4,Zbuff1);
516
517 //Intermediate results from the four parts of the sum.
518#pragma unroll
519 for(unsigned short c=0;c<nc*nc;c++)
520 F_int[c]+=Zbuff2[c];
521 //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
522 W0[0]=W7[0]*W4[0]-W7[1]*conj(W4[1]); W0[1]=W7[0]*W4[1]+W7[1]*conj(W4[0]);
523 W1[0]=W5[0]*W6[0]-W5[1]*conj(W6[1]); W1[1]=W5[0]*W6[1]+W5[1]*conj(W6[0]);
524 //Store W8 in W0
525 W0[0]-=W1[0]; W0[1]-=W1[1];
526
527 //Now load @f$@Z_0=X_{\mu\nu}(x)@f$
528 Device::GetBilinear(Z,Xmn,i);
529 Device::cuGLeft(Zbuff1,W0,Z);
530 //And sum intermediate
531#pragma unroll
532 for(unsigned short c=0;c<nc*nc;c++)
533 F_int[c]+=Zbuff1[c];
534
535 //Now load @f$@Z_1=X_{\mu\nu}(x)@f$
536 ind=iu[i+kvol*mu];
537 Device::GetBilinear(Z,Xmn,ind);
538 Device::cuGRight(Zbuff1,W0,Z);
539 //And sum intermediate
540#pragma unroll
541 for(unsigned short c=0;c<nc*nc;c++){
542 F_int[c]+=Zbuff1[c];
543 F_int[c]*=-I_f;
544 }
545
546 //Excellent. Now we just need to multiply by the derivative term
547 W0[0]=u11t[i+kvolHalo*mu]; W0[1]=u12t[i+kvolHalo*mu];
548 for(unsigned short gen=0;gen<nadj;gen++){
549 W1[0]=W0[0]; W1[1]=W0[1];
550 cuByGenLeft(W1,gen);
551 Device::cuGLeft(Zbuff1,W1,F_int);
552 //Sum of the real part of the trace.
553 float dSdpis=creal(Zbuff1[0])+creal(Zbuff1[3]);
554 //tmp lets us control the number of registers explictly
555 if(mu<nu)
556 dSdpi[i+kvol*(gen*ndim+mu)] -=akappa*dSdpis/4.0f;
557 else
558 dSdpi[i+kvol*(gen*ndim+mu)] +=akappa*dSdpis/4.0f;
559
560 }
561 }
562 return;
563 }
564
565 //Clover multiplication
580 template <typename T>
581 __global__ void ByClover(complex<T> *phi, complex<T> *r, complex<T> *clover1, complex<T> *clover2, complex<T> *sigval, const float akappa, unsigned short *sigin, bool dag){
582 const unsigned int gsize = gridDim.x*gridDim.y*gridDim.z;
583 const unsigned int bsize = blockDim.x*blockDim.y*blockDim.z;
584 const unsigned int blockId = blockIdx.x+ blockIdx.y * gridDim.x+ gridDim.x * gridDim.y * blockIdx.z;
585 const unsigned int bthreadId= (threadIdx.z * blockDim.y+ threadIdx.y)* blockDim.x+ threadIdx.x;
586 const unsigned int gthreadId= blockId * bsize+bthreadId;
587
588 for(unsigned int i=gthreadId;i<kvol;i+=bsize*gsize){
589 //Prefetched r and Phi array
590 complex<T> phi_s[ngorkov][nc];
591#pragma unroll
592 for(unsigned short igorkov=0; igorkov<ngorkov; igorkov++)
593 for(unsigned short c=0; c<nc; c++){
594 phi_s[igorkov][c]=0;
595 }
596 complex<T> r_s[nc];
597 complex<T> clov_s[nc];
598#pragma unroll
599 for(unsigned short clov=0;clov<6;clov++){
600 clov_s[0]=clover1[clov*kvol+i]; clov_s[1]=clover2[clov*kvol+i];
601 for(unsigned short igorkov=0; igorkov<ngorkov; igorkov++){
602 //Mod 4 done bitwise. In general n mod 2^m = n & (2^m-1)
603 const unsigned short idirac = igorkov&3;
604 const unsigned short sind = (igorkov<4) ? sigin[clov*ndirac+idirac] : sigin[clov*ndirac+idirac]+4;
605#pragma unroll
606 for(unsigned short c=0; c<nc; c++)
607 r_s[c]= r[i+kvolHalo*(sind*nc+c)];
609 phi_s[igorkov][0]+=sigval[clov*ndirac+idirac]*(creal(clov_s[0])*r_s[0]+clov_s[1]*r_s[1]);
610 //Clover is in the Lie Algebra, not Lie group. So signs are correct here.
611 phi_s[igorkov][1]+=sigval[clov*ndirac+idirac]*(conj(clov_s[1])*r_s[0]-creal(clov_s[0])*r_s[1]);
612 }
613 }
614#pragma unroll
615 for(unsigned short igorkov=0; igorkov<ngorkov; igorkov++)
616 for(unsigned short c=0; c<nc; c++){
619 //dag is just to do with the output layout and if it has a halo
620 if(dag)
621 phi[i+kvol*(nc*igorkov+c)]+=akappa*phi_s[igorkov][c];
622 else
623 phi[i+kvolHalo*(nc*igorkov+c)]+=akappa*phi_s[igorkov][c];
624 }
625 }
626 return;
627 }
628
643 template <typename T>
644 __global__ void HbyClover(complex<T> *phi, complex<T> *r, complex<T> *clover1, complex<T> *clover2,complex<T> *sigval, const float akappa, unsigned short *sigin,bool dag){
645 const unsigned int gsize = gridDim.x*gridDim.y*gridDim.z;
646 const unsigned int bsize = blockDim.x*blockDim.y*blockDim.z;
647 const unsigned int blockId = blockIdx.x+ blockIdx.y * gridDim.x+ gridDim.x * gridDim.y * blockIdx.z;
648 const unsigned int bthreadId= (threadIdx.z * blockDim.y+ threadIdx.y)* blockDim.x+ threadIdx.x;
649 const unsigned int gthreadId= blockId * bsize+bthreadId;
650
651 for(unsigned int i=gthreadId;i<kvol;i+=bsize*gsize){
652 //Prefetched r and Phi array
653 complex<T> phi_s[ndirac*nc];
654#pragma unroll
655 for(unsigned short idirac=0; idirac<ndirac*nc; idirac+=nc)
656 for(unsigned short c=0; c<nc; c++){
657 phi_s[idirac+c]=0;
658 }
659 complex<T> r_s[nc]; complex<T> clov_s[nc];
660#pragma unroll
661 for(unsigned short clov=0;clov<6;clov++){
662 clov_s[0]=clover1[clov*kvol+i]; clov_s[1]=clover2[clov*kvol+i];
663 for(unsigned short idirac=0; idirac<ndirac*nc; idirac+=nc){
664 const unsigned short sind = sigin[clov*ndirac+(idirac>>1)] << (nc-1);
665#pragma unroll
666 for(unsigned short c=0; c<nc; c++){
667 r_s[c]= r[i+kvolHalo*(sind+c)];
668 }
670 const complex<T> sig=sigval[clov*ndirac+(idirac>>1)];
671 phi_s[idirac+0]+=sig*(creal(clov_s[0])*r_s[0]+clov_s[1]*r_s[1]);
672 //Clover is in the Lie Algebra, not Lie group. So signs are correct here.
673 phi_s[idirac+1]+=sig*(conj(clov_s[1])*r_s[0]-creal(clov_s[0])*r_s[1]);
674 }
675 }
676#pragma unroll
677 for(unsigned short idirac=0; idirac<ndirac*nc; idirac+=nc)
678 for(unsigned short c=0; c<nc; c++)
681 //dag is just to do with the output layout and if it has a halo
682 if(dag)
683 phi[i+kvol*(c+idirac)]+=akappa*phi_s[idirac+c];
684 else
685 phi[i+kvolHalo*(c+idirac)]+=akappa*phi_s[idirac+c];
686 }
687 return;
688 }
689}
690using namespace Kernels;
691
692//Calling Wrappers
693//This gets called by C so cannot be templated...
694int cuClover(Complex_f *clover[nc],Complex_f *ut[nc], unsigned int *iu, unsigned int *id){
695 const char funcname[]="cuClover";
696#ifdef _DEBUG
697 cudaMallocManaged((void **)&clover[0],6*kvol*sizeof(Complex_f),cudaMemAttachGlobal);
698 cudaMallocManaged((void **)&clover[1],6*kvol*sizeof(Complex_f),cudaMemAttachGlobal);
699#else
700 cudaMallocAsync((void **)&clover[0],6*kvol*sizeof(Complex_f),streams[0]);
701 cudaMallocAsync((void **)&clover[1],6*kvol*sizeof(Complex_f),streams[1]);
702#endif
703 for(unsigned short mu=0;mu<ndim-1;mu++)
704 for(unsigned short nu=mu+1;nu<ndim;nu++)
705 if(mu!=nu){
706 //Clover index
707 unsigned short clov = (mu==0) ? nu-1 :mu+nu;
708 //Allocate clover memory
709 //Note that the clover is completely local, so doesn't need a halo for MPI
710 Full_Clover<<<dimGrid,dimBlock,0,streams[clov]>>>(clover[0]+clov*kvol,clover[1]+clov*kvol,\
711 ut[0],ut[1],iu,id,mu,nu);
712 }
714 return 0;
715}
716void cuByClover(Complex *phi, Complex *r, Complex *clover[nc],Complex *sigval,const float akappa, unsigned short *sigin, bool dag){
717 Kernels::ByClover<<<dimGrid,dimBlock>>>(phi,r,clover[0],clover[1],sigval,akappa,sigin,dag);
718}
719void cuHbyClover(Complex *phi, Complex *r, Complex *clover[nc],Complex *sigval, const float akappa, unsigned short *sigin, bool dag){
720 Kernels::HbyClover<<<dimGrid,dimBlock>>>(phi,r,clover[0],clover[1],sigval,akappa,sigin,dag);
721}
722void cuByClover_f(Complex_f *phi, Complex_f *r, Complex_f *clover[nc],Complex_f *sigval, const float akappa, unsigned short *sigin,bool dag){
723 Kernels::ByClover<<<dimGrid,dimBlock>>>(phi,r,clover[0],clover[1],sigval,akappa,sigin,dag);
724}
725void cuHbyClover_f(Complex_f *phi, Complex_f *r, Complex_f *clover[nc],Complex_f *sigval, const float akappa, unsigned short *sigin,bool dag){
726 Kernels::HbyClover<<<dimGrid,dimBlock>>>(phi,r,clover[0],clover[1],sigval,akappa,sigin,dag);
727}
728
729void cuCalcXmunu(Bilinear_a Xmunu, Complex_f *X1, Complex_f *X2, const Complex_f *sigval, const unsigned short *sigin,\
730 const unsigned short mu, const unsigned short nu){
731 //Get sign and index of @f$\sigma_{\mu\nu}@f correct
732 unsigned short clov = (mu==0) ? nu-1 : mu+nu;
733 Kernels::cuCalcXmunu<<<dimGrid,dimBlock,0,streams[clov]>>>(Xmunu,X1,X2,sigval,sigin,clov);
734 return;
735}
736int cuClov_Force(double *dSdpi, Complex_f *ut[nc], Complex_f *X1, Complex_f *X2, const Complex_f *sigval,\
737 const unsigned short *sigin, const unsigned int *iu, const unsigned int *id, const float akappa){
738 const char funcname[]="Clov_Force";
739
740 const unsigned short nclov = (ndim-1)*(ndim-2);
741 //Too many pointers here but not bothered doing it correctly. Overhead is basically zero.
742 Bilinear_a Xmn[nclov];
743 //Allocate half-leaf memory. We will have one stream for each direction
744 for(unsigned short mu=0;mu<ndim-1;mu++)
745 for(unsigned short nu=mu;nu<ndim;nu++)
746 if(mu!=nu){
747 //Get sign and index of @f$\sigma_{\mu\nu}@f correct
748 unsigned short clov = (mu==0) ? nu-1 : mu+nu;
749 //Allocate and evaluate @f$X_{\mu\nu}@f$ terms
750 cudaMallocAsync((void **)&Xmn[clov].diag,nc*kvol*sizeof(float),streams[clov]);
751 cudaMallocAsync((void **)&Xmn[clov].offd,nc*kvol*sizeof(complex<float>),streams[clov]);
752 Kernels::cuCalcXmunu<<<dimGrid,dimBlock,0,streams[clov]>>>(Xmn[clov],X1,X2,sigval,sigin,clov);
753 }
755
756 for(unsigned short mu=0;mu<ndim;mu++)
757 for(unsigned short nu=0;nu<ndim;nu++)
758 if(mu!=nu){
759 unsigned short clov=0;
760 if(mu<nu)
761 clov = (mu==0) ? nu-1 : mu+nu;
762 else
763 clov = (nu==0) ? mu-1 : mu+nu;
764 //Compute force for @f$\mu\nu@f$ and @f$\nu\mu@f$
765 Kernels::Clov_Force<<<dimGrid,dimBlock,0,streams[mu]>>>(dSdpi,ut[0],ut[1],Xmn[clov],\
766 sigval,sigin,iu,id,akappa,mu,nu);
767
768 }
770 for(unsigned short clov=0;clov<nclov;clov++){
771 cudaFreeAsync(Xmn[clov].diag,NULL); cudaFreeAsync(Xmn[clov].offd,NULL);
772 }
774 return 0;
775}
Routines needed for Clover improved wilson fermions.
__device__ void GetBilinear(Complex_f Z[nc *nc], Bilinear_a Xmn, unsigned int ind)
Loads the compacted bilinear form into a complex valued matrix.
Definition cuclover.cu:272
__global__ void Clov_Force(double *dSdpi, const complex< T > *u11t, const complex< T > *u12t, Bilinear_a Xmn, const complex< T > *sigval, const unsigned short *sigin, const unsigned int *iu, const unsigned int *id, const float akappa, const unsigned short mu, const unsigned short nu)
cuGets the clover contribution to the force
Definition cuclover.cu:427
__device__ void cuByGenLeft(T a[nc], const unsigned short gen)
Multiply leaf (or part of one) by generator from left.
Definition cuclover.cu:24
__device__ void cuByGenRight(T a[nc], const unsigned short gen)
Multiply leaf (or part of one) by generator from right.
Definition cuclover.cu:56
__device__ void cuGSandwich(T out[4], T tmp[4], const T Gl[2], const T X[4], const T Gr[2])
Multiplies by a gauge field from the left and the right.
Definition cuclover.cu:257
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
__device__ void cuGLeft(T out[4], const T G[2], const T X[4])
Multiplies by a gauge field from the left.
Definition cuclover.cu:224
__device__ void cuGRight(T out[4], const T G[2], const T X[4])
Multiplies by a gauge field from the right.
Definition cuclover.cu:240
__global__ void cuCalcXmunu(Bilinear_a Xmunu, const complex< T > *X1, const complex< T > *X2, const complex< T > *sigval, const unsigned short *sigin, const unsigned short clov)
Gets for the clover force.
Definition cuclover.cu:369
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
__global__ void ByClover(complex< T > *phi, complex< T > *r, complex< T > *clover1, complex< T > *clover2, complex< T > *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 cuclover.cu:581
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 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
__global__ void HbyClover(complex< T > *phi, complex< T > *r, complex< T > *clover1, complex< T > *clover2, complex< T > *sigval, const float akappa, unsigned short *sigin, bool dag)
Clover analogue of the Hdslash operation. The H in front is for half, as we only act on the fermions ...
Definition cuclover.cu:644
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
__device__ void Leaf(complex< T > *u11t, complex< T > *u12t, complex< T > Leaves[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 cuclover.cu:153
__global__ void Half_Leaves(complex< T > *hLeaves0, complex< T > *hLeaves1, complex< T > *u11t, complex< T > *u12t, 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 cuclover.cu:294
__global__ void Full_Clover(complex< T > *clover1, complex< T > *clover2, complex< T > *u11t, complex< T > *u12t, unsigned int *iu, unsigned int *id, int mu, int nu)
Calculates the clovers in all directions at all sites .
Definition cuclover.cu:323
__device__ void Half_Leaf(complex< T > Leaves[nc], complex< T > *u11t, complex< T > *u12t, complex< T > 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 cuclover.cu:91
__device__ __forceinline__ T conj(const T &z)
Complex Conjugation.
Definition cusu2hmc.cu:33
CUDA Device code.
Definition cubosonic.cu:13
CUDA Kernels.
Definition cubosonic.cu:47
#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
#define cudaDeviceSynchronise()
Get rid of that bastardised yankee English.
Definition sizes.h:53
#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_f
Define I in single precision.