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 445 of file clover.c.

446 {
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}
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
#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 542 of file clover.c.

543 {
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}
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 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 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 729 of file cuclover.cu.

730 {
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}
__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 736 of file cuclover.cu.

737 {
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}
__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 440 of file clover.c.

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

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 503 of file clover.c.

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

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 518 of file clover.c.

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

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 536 of file clover.c.

536 {
537 GRight(tmp,Gr,X);
538 GLeft(out,Gl,tmp);
539 return;
540}

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

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