su2hmc
Loading...
Searching...
No Matches
Clover Force
Collaboration diagram for Clover Force:

Classes

struct  Bilinear_a
 Structure of arrays for Hermitian bilinear \(X_{\mu\nu}\) in memory. More...
struct  Bilinear
 Hermitian bilinear \(X_{\mu\nu}\) on the local stack. More...

Functions

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 \(X_{\mu\nu}\) for the clover force.
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.
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.
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.
static void GetBilinear (Complex_f Z[nc *nc], Bilinear_a Xmn, unsigned int ind)
 Loads the compacted bilinear form into a \(2\times2\) complex valued matrix.
static void GLeft (Complex_f out[4], const Complex_f G[2], const Complex_f X[4])
 Multiplies \( X_{\mu\nu}\) by a gauge field from the left.
static void GRight (Complex_f out[4], const Complex_f G[2], const Complex_f X[4])
 Multiplies \( X_{\mu\nu}\) by a gauge field from the right.
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 \( X_{\mu\nu}\) by a gauge field from the left and the right.
template<typename T>
__device__ void Device::cuByGenLeft (T a[nc], const unsigned short gen)
 Multiply leaf (or part of one) by generator from left.
template<typename T>
__device__ void Device::cuByGenRight (T a[nc], const unsigned short gen)
 Multiply leaf (or part of one) by generator from right.
template<typename T>
__device__ void Device::cuGLeft (T out[4], const T G[2], const T X[4])
 Multiplies \( X_{\mu\nu}\) by a gauge field from the left.
template<typename T>
__device__ void Device::cuGRight (T out[4], const T G[2], const T X[4])
 Multiplies \( X_{\mu\nu}\) by a gauge field from the right.
template<typename T>
__device__ void Device::cuGSandwich (T out[4], T tmp[4], const T Gl[2], const T X[4], const T Gr[2])
 Multiplies \( X_{\mu\nu}\) by a gauge field from the left and the right.
__device__ void Device::GetBilinear (Complex_f Z[nc *nc], Bilinear_a Xmn, unsigned int ind)
 Loads the compacted bilinear form into a \(2\times2\) complex valued matrix.
template<typename T>
__global__ void Kernels::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 \(X_{\mu\nu}\) for the clover force.
template<typename T>
__global__ void Kernels::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

Detailed Description

Function Documentation

◆ CalcXmunu()

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 \(X_{\mu\nu}\) for the clover force.

Parameters
[out]XmunuAll Xmunu values
[in]X1Congrad output \(\left(M^\dagger M\right)\Phi\)
[in]X2\(M\left(M^\dagger M\right)^{-1}\Phi\)
[in]sigval\(\sigma_{\mu\nu}\) scaled by \(\frac{c_\text{SW}}{2}\)
[in]siginDirac index of \(\sigma_{\mu\nu}\)
[in]mu,nuLattice directions
Postcondition
Bilinears written to Xmunu

Definition at line 437 of file clover.c.

438 {
439 const char funcname[] = "Xmunu";
440#ifdef USE_GPU
441 cuCalcXmunu(Xmunu,X1,X2,sigval,sigin,mu,nu);
442#else
443 unsigned short clov;
444 //Get sign and index of @f$\sigma_{\mu\nu}@f$ correct
445 clov = (mu==0) ? nu-1 : mu+nu;
446#pragma omp parallel for simd aligned(X1,X2:AVX)
447 for(unsigned int i=0;i<kvol;i++){
448 //Buffer. Four registers
449 Bilinear Xmn;
450 Xmn.diag[0]=Xmn.diag[1]=0;Xmn.offd=0;
451 for(unsigned short idirac=0; idirac<ndirac*nc; idirac+=nc){
452 const unsigned short sind = sigin[clov*ndirac+(idirac>>1)]<<1;
453 const Complex_f sig = sigval[clov*ndirac+(idirac>>1)];
454#pragma unroll
455 for(unsigned short c1=0;c1<nc;c1++){
456 //Spinors (rows) So we only load from memory once.
457 const Complex_f X1s = X1[i+kvolHalo*(sind+c1)];
458 const Complex_f X2s = X2[i+kvolHalo*(sind+c1)];
459#pragma unroll
460 for(unsigned short c2=0;c2<nc;c2++){
461 //The second off diagonal term is the conjugate of the first
462 if(c1==1&&c2==0)
463 continue;
464 //Conjugated spinor (columns).
465 const Complex_f X1c = conjf(X1[i+kvolHalo*(idirac+c2)]);
466 const Complex_f X2c = conjf(X2[i+kvolHalo*(idirac+c2)]);
467 if(c1==c2)
468 Xmn.diag[c1]+=creal(sig*(X2s*X1c+X1s*X2c));
469 else
470 Xmn.offd+=sig*(X2s*X1c+X1s*X2c);
471
472 }
473 }
474 }
475 //And write back to global memory.
476#pragma unroll
477 //Write the diagonals
478 for(unsigned short c=0;c<nc;c++)
479 Xmunu.diag[i+kvolHalo*c]=Xmn.diag[c];
480 //And the off diagonal terms
481 Xmunu.offd[i]=Xmn.offd;
482 }
483#endif
484 return;
485}
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:725
#define nc
Colours.
Definition sizes.h:182
#define kvol
Sublattice volume.
Definition sizes.h:163
#define ndirac
Dirac indices.
Definition sizes.h:186
#define Complex_f
Single precision complex number.
Definition sizes.h:62
#define kvolHalo
Subvolume + halo size.
Definition sizes.h:234
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
#define creal(z)
Extract Real Component using C standard notation.

References Complex_f, creal, cuCalcXmunu(), Bilinear::diag, Bilinear_a::diag, kvol, kvolHalo, nc, ndirac, Bilinear::offd, and Bilinear_a::offd.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Clov_Force() [1/2]

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.

Parameters
[in,out]dSdpiForce
[in]utGauge fields
[in]X1Congrad output \(\left(M^\dagger M\right)\Phi\)
[in]X2\(M\left(M^\dagger M\right)^{-1}\Phi\)
[in]sigval\(\sigma_{\mu\nu}\) scaled by \(\frac{c_\text{SW}}{2}\)
[in]siginDirac index of \(\sigma_{\mu\nu}\)
[in]iu,idNeighbouring sites
[in]akappaHopping parameter
Postcondition
Force contribution added to dSdpi

Definition at line 534 of file clover.c.

535 {
536 const char funcname[] = "Clov_Force";
537#ifdef USE_GPU
538 cuClov_Force(dSdpi,ut,X1,X2,sigval,sigin,iu,id,akappa);
539#else
540 //Allocate the @f$X_{\mu\nu}@f$ array
541 unsigned short nclov=6; unsigned short clov=0;
542 Bilinear_a Xmn[nclov];
543 //And get the @f$X_{\mu\nu}@f$ values
544 //Loop over @f$\mu@f$ and @f$\nu@f$. Symmetry means we actually only need half the terms
545 for(unsigned short mu=0;mu<ndim-1;mu++)
546 for(unsigned short nu=mu+1;nu<ndim;nu++)
547 if(mu!=nu){
548 clov = (mu==0) ? nu-1 : mu+nu;
549 Xmn[clov].diag=(float *)aligned_alloc(AVX,2*kvolHalo*sizeof(float));
550 Xmn[clov].offd=(Complex_f *)aligned_alloc(AVX,kvolHalo*sizeof(Complex_f));
551 CalcXmunu(Xmn[clov],X1,X2,sigval,sigin,mu,nu);
552#if(nproc>1)
553 SHalo_swap_all(Xmn[clov].diag,2);
554 CHalo_swap_all(Xmn[clov].offd,1);
555#endif
556 }
557 for(unsigned short mu=0;mu<ndim;mu++)
558 for(unsigned short nu=0;nu<ndim;nu++)
559 if(mu!=nu){
560 if(mu<nu)
561 clov = (mu==0) ? nu-1 : mu+nu;
562 else
563 clov = (nu==0) ? mu-1 : mu+nu;
564#pragma omp parallel for
565 for(unsigned int i=0;i<kvol;i++){
566 //This is where it gets messy. Using HiRep/OpenQCD labelling for different intermediate values
567 //But recycling to reduce register pressure on GPU
568 //First up, W0, W1 and W6 match their Documentation values
569 Complex_f W0[2], W1[2], W6[2];
570 //Get the correct site.
571 unsigned int ind = id[i+kvol*nu];
572 //Gauge field @f$U_\nu\left(i-\hat{\nu}\right)
573 W1[0]=ut[0][ind+kvolHalo*nu]; W1[1]=ut[1][ind+kvolHalo*nu];
574
575 //@f$Z_2=X_{\mu\nu}\left(x-\hat{\nu}\right)@f$
576 Complex_f Z[nc*nc];
577 GetBilinear(Z,Xmn[clov],ind);
578
579 //W0 is @f$U^\dagger_\mu\left(x-\hat{nu}\right)@f$
580 W0[0]=conjf(ut[0][ind+kvolHalo*mu]); W0[1]=-ut[1][ind+kvolHalo*mu];
581
582 //Need a temporary Z buffers for the intermediate result
583 Complex_f Zbuff1[nc*nc], Zbuff2[nc*nc];
584 GSandwich(Zbuff1,Zbuff2,W0,Z,W1);
585
586 //@f$W_6=W_0 W_1@f$
587 W6[0]=W0[0]*W1[0]-W0[1]*conjf(W1[1]); W6[1]=W0[0]*W1[1]+W0[1]*conjf(W1[0]);
588
589 //Z_3 is the @f$X_{\mu\nu}\left(x+\hat{\mu}-\hat{\nu}\right)@f$. Store in Z
590 ind=iu[ind+kvol*mu];
591 GetBilinear(Z,Xmn[clov],ind);
592
593 //Need a second Zbuffer for another intermediate result.
594 GRight(Zbuff2,W6,Z);
595 //Sum the two results into Zbuff1. Then scale by -W5
596#pragma unroll
597 for(unsigned short c=0;c<nc*nc;c++)
598 Zbuff1[c]+=Zbuff2[c];
599 //W5 is @f$U^\dagger_\nu\left(x+\hat{\mu}-\hat{\nu}\right)@f$
600 Complex_f W5[2];
601 W5[0]=conjf(ut[0][ind+kvolHalo*nu]); W5[1]=-ut[1][ind+kvolHalo*nu];
602 //Now multiply by @f$W_5@f$ from the left into Zbuff2
603 GLeft(Zbuff2,W5,Zbuff1);
604
605 //Intermediate results from the four parts of the sum.
606 Complex_f F_int[4];
607#pragma unroll
608 for(unsigned short c=0;c<nc*nc;c++)
609 //Negative as it is @f$-W_5@f$
610 F_int[c]=-Zbuff2[c];
611
612 //Now we repeat for the last term in the sum. Recycling along the way.
613 //First store @f$W_2=U_\nu\left(x+\hat{\mu}\right)@f$ into W0.
614 ind=iu[i+kvol*mu];
615 W0[0]=ut[0][ind+kvolHalo*nu]; W0[1]=ut[1][ind+kvolHalo*nu];
616 //@f$W_3=U^\dagger_\mu\left(x+\hat{\nu}\right)@f$. Storing it in W1
617 ind=iu[i+kvol*nu];
618 W1[0]=conjf(ut[0][ind+kvolHalo*mu]); W1[1]=-ut[1][ind+kvolHalo*mu];
619 //@f$Z_4=X_{\mu\nu}\left(x+\hat{\mu}+\hat{\nu}\right)@f$. Storing in Z
620 ind=iu[ind+kvol*mu];
621 GetBilinear(Z,Xmn[clov],ind);
622 //Calculate and write into Zbuff1
623 GSandwich(Zbuff1,Zbuff2,W0,Z,W1);
624
625 //@f$W_7=W_0 W_1@f$
626 Complex_f W7[2];
627 W7[0]=W0[0]*W1[0]-W0[1]*conjf(W1[1]); W7[1]=W0[0]*W1[1]+W0[1]*conjf(W1[0]);
628 //@f$Z_5=X_{\mu\nu}\left(x+\hat{\nu}\right)@f$
629 ind=iu[i+kvol*nu];
630 GetBilinear(Z,Xmn[clov],ind);
631 //And calculate the second term
632 GLeft(Zbuff2,W7,Z);
633 //Sum the two results into Zbuff1.
634#pragma unroll
635 for(unsigned short c=0;c<nc*nc;c++)
636 Zbuff1[c]+=Zbuff2[c];
637
638 //W4 is @f$U^\dagger_\nu\left(x\right)@f$
639 Complex_f W4[2];
640 W4[0]=conjf(ut[0][i+kvolHalo*nu]); W4[1]=-ut[1][i+kvolHalo*nu];
641 //Now multiply by @f$W_4@f$ from the right into Zbuff2
642 GRight(Zbuff2,W4,Zbuff1);
643
644 //Intermediate results from the four parts of the sum.
645#pragma unroll
646 for(unsigned short c=0;c<nc*nc;c++)
647 F_int[c]+=Zbuff2[c];
648 //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
649 W0[0]=W7[0]*W4[0]-W7[1]*conjf(W4[1]); W0[1]=W7[0]*W4[1]+W7[1]*conjf(W4[0]);
650 W1[0]=W5[0]*W6[0]-W5[1]*conjf(W6[1]); W1[1]=W5[0]*W6[1]+W5[1]*conjf(W6[0]);
651 //Store W8 in W0
652 W0[0]-=W1[0]; W0[1]-=W1[1];
653
654 //Now load @f$Z_0=X_{\mu\nu}(x)@f$
655 GetBilinear(Z,Xmn[clov],i);
656 GLeft(Zbuff1,W0,Z);
657 //And sum intermediate
658#pragma unroll
659 for(unsigned short c=0;c<nc*nc;c++)
660 F_int[c]+=Zbuff1[c];
661
662 //Now load @f$Z_1=X_{\mu\nu}\left(x+\hat{mu})@f$
663 ind=iu[i+kvol*mu];
664 GetBilinear(Z,Xmn[clov],ind);
665 GRight(Zbuff1,W0,Z);
666 //And sum intermediate
667#pragma unroll
668 for(unsigned short c=0;c<nc*nc;c++){
669 F_int[c]+=Zbuff1[c];
670 //See if this works...
671 F_int[c]*=-I;
672 }
673
674 //Excellent. Now we just need to multiply by the derivative term
675 W0[0]=ut[0][i+kvolHalo*mu]; W0[1]=ut[1][i+kvolHalo*mu];
676 for(unsigned short gen=0;gen<nadj;gen++){
677 W1[0]=W0[0]; W1[1]=W0[1];
678 ByGenLeft(W1,gen);
679 GLeft(Zbuff1,W1,F_int);
680 //Sum of the real part of the trace.
681 float dSdpis=crealf(Zbuff1[0])+crealf(Zbuff1[3]);
682 if(mu<nu)
683 dSdpi[i+kvol*(gen*ndim+mu)] -=akappa*dSdpis/4.0f;
684 else
685 dSdpi[i+kvol*(gen*ndim+mu)] +=akappa*dSdpis/4.0f;
686 }
687 }
688 }
689 for(clov=0;clov<nclov;clov++){
690 free(Xmn[clov].diag); free(Xmn[clov].offd);
691 }
692#endif
693 return;
694}
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:510
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:437
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:495
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:732
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:528
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:432
void ByGenLeft(Complex_f a[nc], const unsigned short gen)
Multiply leaf (or part of one) by generator from left.
Definition clover.c:9
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 nadj
adjacent spatial indices
Definition sizes.h:184
#define ndim
Dimensions.
Definition sizes.h:188
Structure of arrays for Hermitian bilinear in memory.
Definition clover.h:30
#define I
Define I in double precision using C standard notation.

References AVX, ByGenLeft(), CalcXmunu(), CHalo_swap_all(), Complex_f, cuClov_Force(), Bilinear_a::diag, GetBilinear(), GLeft(), GRight(), GSandwich(), I, kvol, kvolHalo, nadj, nc, ndim, Bilinear_a::offd, and SHalo_swap_all().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Clov_Force() [2/2]

template<typename T>
__global__ void Kernels::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

Parameters
[out]dSdpiForce.
[in]u11t,u12tGauge fields
[in]XmnPseudofermion bilinear contraction
[in]sigval\(\sigma_{\mu\nu}\) scaled by \(\frac{c_\text{SW}}{2}\)
[in]siginDirac index of \(\sigma_{\mu\nu}\)
[in]iu,idNeighbouring sites
[in]akappaHopping Parameter
[in]mu,nuClover direction
Postcondition
Clover force is added to dSdpi

Definition at line 427 of file cuclover.cu.

429 {
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 }
__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
__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 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
__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
__device__ __forceinline__ T conj(const T &z)
Complex Conjugation.
Definition cusu2hmc.cu:33
#define I_f
Define I in single precision.

References conj(), creal, Device::cuByGenLeft(), Device::cuGLeft(), Device::cuGRight(), Device::cuGSandwich(), Device::GetBilinear(), I_f, kvol, kvolHalo, nadj, nc, and ndim.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cuByGenLeft()

template<typename T>
__device__ void Device::cuByGenLeft ( T a[nc],
const unsigned short gen )

Multiply leaf (or part of one) by generator from left.

The leaves contributing to each force term need to be scaled by the generator, but the generator appears at different points in each leaf. This routine multiples by the generator from the left side.

Parameters
[in,out]aThe leaf or partial leaf. Result saved in place
[in]genWhat generator are we multiplying by?

\(i\sigma_x\)

\(i\sigma_y\)

\(i\sigma_z\)

Definition at line 24 of file cuclover.cu.

24 {
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 }

References conj(), and nc.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cuByGenRight()

template<typename T>
__device__ void Device::cuByGenRight ( T a[nc],
const unsigned short gen )

Multiply leaf (or part of one) by generator from right.

The leaves contributing to each force term need to be scaled by the generator, but the generator appears at different points in each leaf. This routine multiples by the generator from the right side.

Parameters
[in,out]aThe leaf or partial leaf. Result saved in place
[in]genWhat generator are we multiplying by?

\(i\sigma_x\)

\(i\sigma_y\)

\(i\sigma_z\)

Definition at line 56 of file cuclover.cu.

56 {
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 }

References nc.

◆ cuCalcXmunu() [1/2]

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.

Parameters
[out]XmunuAll Xmunu values
[in]X1Congrad output \(\left(M^\dagger M\right)\Phi\)
[in]X2\(M\left(M^\dagger M\right)^{-1}\Phi\)
[in]sigval\(\sigma_{\mu\nu}\) scaled by \(\frac{c_\text{SW}}{2}\)
[in]siginDirac index of \(\sigma_{\mu\nu}\)
[in]mu,nuLattice directions
Postcondition
Bilinears written to Xmunu

Definition at line 725 of file cuclover.cu.

726 {
727 //Get sign and index of @f$\sigma_{\mu\nu}@f correct
728 unsigned short clov = (mu==0) ? nu-1 : mu+nu;
729 Kernels::cuCalcXmunu<<<dimGrid,dimBlock,0,streams[clov]>>>(Xmunu,X1,X2,sigval,sigin,clov);
730 return;
731}
__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

References Complex_f, and Kernels::cuCalcXmunu().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cuCalcXmunu() [2/2]

template<typename T>
__global__ void Kernels::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 \(X_{\mu\nu}\) for the clover force.

Parameters
[out]XmunuAll Xmunu values
[in]X1Congrad output \(\left(M^\dagger M\right)\Phi\)
[in]X2\(M\left(M^\dagger M\right)^{-1}\Phi\)
[in]sigval\(\sigma_{\mu\nu}\) scaled by \(\frac{c_\text{SW}}{2}\)
[in]siginDirac index of \(\sigma_{\mu\nu}\)
[in]clovIndex of clover being used
Postcondition
Contents of Xmunu overwritten

Definition at line 369 of file cuclover.cu.

370 {
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 }

References conj(), Bilinear::diag, Bilinear_a::diag, kvol, kvolHalo, nc, ndirac, Bilinear::offd, and Bilinear_a::offd.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cuClov_Force()

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.

Parameters
[in,out]dSdpiForce
[in]utGauge fields
[in]X1\(\left(M^\dagger M\right)^{-1} \Psi\)
[in]X2\(M\left(M^\dagger M\right)^{-1} \Psi\)
[in]sigval\( \sigma_{\mu\nu}\) entries scaled by \(c_sw\)
[in]siginWhat element of the spinor is multiplied by row idirac each sigma matrix?
[in]iu,idUp/down indices
[in]akappaHopping parameter
Returns
Zero on success, integer error code otherwise
Postcondition
Force contribution added to dSdpi

Definition at line 732 of file cuclover.cu.

733 {
734 const char funcname[]="Clov_Force";
735
736 const unsigned short nclov = (ndim-1)*(ndim-2);
737 //Too many pointers here but not bothered doing it correctly. Overhead is basically zero.
738 Bilinear_a Xmn[nclov];
739 //Allocate half-leaf memory. We will have one stream for each direction
740 for(unsigned short mu=0;mu<ndim-1;mu++)
741 for(unsigned short nu=mu;nu<ndim;nu++)
742 if(mu!=nu){
743 //Get sign and index of @f$\sigma_{\mu\nu}@f correct
744 unsigned short clov = (mu==0) ? nu-1 : mu+nu;
745 //Allocate and evaluate @f$X_{\mu\nu}@f$ terms
746 cudaMallocAsync((void **)&Xmn[clov].diag,nc*kvol*sizeof(float),streams[clov]);
747 cudaMallocAsync((void **)&Xmn[clov].offd,nc*kvol*sizeof(complex<float>),streams[clov]);
748 Kernels::cuCalcXmunu<<<dimGrid,dimBlock,0,streams[clov]>>>(Xmn[clov],X1,X2,sigval,sigin,clov);
749 }
751
752 for(unsigned short mu=0;mu<ndim;mu++)
753 for(unsigned short nu=0;nu<ndim;nu++)
754 if(mu!=nu){
755 unsigned short clov=0;
756 if(mu<nu)
757 clov = (mu==0) ? nu-1 : mu+nu;
758 else
759 clov = (nu==0) ? mu-1 : mu+nu;
760 //Compute force for @f$\mu\nu@f$ and @f$\nu\mu@f$
761 Kernels::Clov_Force<<<dimGrid,dimBlock,0,streams[mu]>>>(dSdpi,ut[0],ut[1],Xmn[clov],\
762 sigval,sigin,iu,id,akappa,mu,nu);
763
764 }
766 for(unsigned short clov=0;clov<nclov;clov++){
767 cudaFreeAsync(Xmn[clov].diag,NULL); cudaFreeAsync(Xmn[clov].offd,NULL);
768 }
770 return 0;
771}
__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
#define cudaDeviceSynchronise()
Get rid of that bastardised yankee English.
Definition sizes.h:53
cudaStream_t streams[ndirac *ndim *nadj]
An array of concurrent GPU streams to keep it busy.
Definition cusu2hmc.cu:29

References Kernels::Clov_Force(), Complex_f, Kernels::cuCalcXmunu(), cudaDeviceSynchronise, kvol, nc, ndim, and streams.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cuGLeft()

template<typename T>
__device__ void Device::cuGLeft ( T out[4],
const T G[2],
const T X[4] )

Multiplies \( X_{\mu\nu}\) by a gauge field from the left.

Parameters
[out]outResult
[in]X\(X_{\mu\nu}(x)\)
[in]GGauge field

Definition at line 224 of file cuclover.cu.

224 {
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 }

References conj().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cuGRight()

template<typename T>
__device__ void Device::cuGRight ( T out[4],
const T G[2],
const T X[4] )

Multiplies \( X_{\mu\nu}\) by a gauge field from the right.

Parameters
[out]outResult
[in]X\(X_{\mu\nu}(x)\)
[in]GGauge field

Definition at line 240 of file cuclover.cu.

240 {
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 }

References conj().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ cuGSandwich()

template<typename T>
__device__ void Device::cuGSandwich ( T out[4],
T tmp[4],
const T Gl[2],
const T X[4],
const T Gr[2] )

Multiplies \( X_{\mu\nu}\) by a gauge field from the left and the right.

Parameters
[out]outResult
[in]tmpBuffer for intermediate result. Passing as an argument to reduce register pressure.
[in]X\(X_{\mu\nu}(x)\)
[in]Gl,GrLeft/Right Gauge fields

Definition at line 257 of file cuclover.cu.

257 {
258 cuGRight(tmp,Gr,X);
259 cuGLeft(out,Gl,tmp);
260 return;
261 }

References cuGLeft(), and cuGRight().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetBilinear() [1/2]

__device__ void Device::GetBilinear ( Complex_f Z[nc *nc],
Bilinear_a Xmn,
unsigned int ind )

Loads the compacted bilinear form into a \(2\times2\) complex valued matrix.

Parameters
[out]ZComplex matrix on stack
[in]XmnHermitian form of \(X_{\mu\nu}\) in memory
[in]indIndex of \(X_{\mu\nu}\) we're interested in
Postcondition
Contents of Z are replaced by bilinear from memory

Definition at line 272 of file cuclover.cu.

272 {
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 }

References Complex_f, conj(), Bilinear_a::diag, kvolHalo, nc, and Bilinear_a::offd.

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GetBilinear() [2/2]

void GetBilinear ( Complex_f Z[nc *nc],
Bilinear_a Xmn,
unsigned int ind )
inlinestatic

Loads the compacted bilinear form into a \(2\times2\) complex valued matrix.

Parameters
[out]ZComplex matrix on stack
[in]XmnHermitian form of \(X_{\mu\nu}\) in memory
[in]indIndex of \(X_{\mu\nu}\) we're interested in
Postcondition
Contents of Z are replaced by bilinear from memory

Definition at line 432 of file clover.c.

432 {
433 Z[0]=Xmn.diag[ind]; Z[3]=Xmn.diag[ind+kvolHalo];
434 Z[1]=Xmn.offd[ind]; Z[2]=conjf(Z[1]);
435 return;
436}

References Complex_f, Bilinear_a::diag, kvolHalo, nc, and Bilinear_a::offd.

Here is the caller graph for this function:

◆ GLeft()

void GLeft ( Complex_f out[4],
const Complex_f G[2],
const Complex_f X[4] )
inlinestatic

Multiplies \( X_{\mu\nu}\) by a gauge field from the left.

Parameters
[out]outResult
[in]X\(X_{\mu\nu}(x)\)
[in]GGauge field

Definition at line 495 of file clover.c.

495 {
496 out[0]=G[0]*X[0]+G[1]*X[2];
497 out[1]=G[0]*X[1]+G[1]*X[3];
498 out[2]=-conj(G[1])*X[0]+conj(G[0])*X[2];
499 out[3]=-conj(G[1])*X[1]+conj(G[0])*X[3];
500 return;
501}

References Complex_f, and conj().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GRight()

void GRight ( Complex_f out[4],
const Complex_f G[2],
const Complex_f X[4] )
inlinestatic

Multiplies \( X_{\mu\nu}\) by a gauge field from the right.

Parameters
[out]outResult
[in]X\(X_{\mu\nu}(x)\)
[in]GGauge field

Definition at line 510 of file clover.c.

510 {
511 out[0]=G[0]*X[0]-conj(G[1])*X[1];
512 out[1]=G[1]*X[0]+conj(G[0])*X[1];
513 out[2]=G[0]*X[2]-conj(G[1])*X[3];
514 out[3]=G[1]*X[2]+conj(G[0])*X[3];
515 return;
516}

References Complex_f, and conj().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ GSandwich()

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] )
inlinestatic

Multiplies \( X_{\mu\nu}\) by a gauge field from the left and the right.

Parameters
[out]outResult
[in,out]tmpBuffer for intermediate result. Passing as an argument to reduce register pressure.
[in]X\(X_{\mu\nu}(x)\)
[in]Gl,GrLeft/Right Gauge fields
Postcondition
Contents of tmp will be changed during the call

Definition at line 528 of file clover.c.

528 {
529 GRight(tmp,Gr,X);
530 GLeft(out,Gl,tmp);
531 return;
532}

References Complex_f, GLeft(), and GRight().

Here is the call graph for this function:
Here is the caller graph for this function: