su2hmc
Loading...
Searching...
No Matches
congrad.c
Go to the documentation of this file.
1
6#include <matrices.h>
7#include <clover.h>
8#include <float.h>
9
23void Q_allocate_f(Complex_f **p_f, Complex_f **x1_f, Complex_f **x2_f, Complex_f **r_f, Complex_f **X1_f){
24 const char funcname[] = "Q_allocate";
25#ifdef USE_GPU
26#ifdef _DEBUG
27 cudaMallocManaged((void **)p_f, kferm2Halo*sizeof(Complex_f),cudaMemAttachGlobal);
28 cudaMallocManaged((void **)x1_f, kferm2Halo*sizeof(Complex_f),cudaMemAttachGlobal);
29 cudaMallocManaged((void **)x2_f, kferm2*sizeof(Complex_f),cudaMemAttachGlobal);
30
31 cudaMallocManaged((void **)r_f, kferm2*sizeof(Complex_f),cudaMemAttachGlobal);
32 cudaMallocManaged((void **)X1_f, kferm2*sizeof(Complex_f),cudaMemAttachGlobal);
33#else
34 //First two have halo exchanges, so getting NCCL working is important
35 cudaMallocAsync((void **)p_f, kferm2Halo*sizeof(Complex_f),streams[0]);
36 cudaMallocAsync((void **)x1_f, kferm2Halo*sizeof(Complex_f),streams[1]);
37 cudaMallocAsync((void **)x2_f, kferm2*sizeof(Complex_f),streams[2]);
38
39 cudaMallocAsync((void **)r_f, kferm2*sizeof(Complex_f),streams[3]);
40 cudaMallocAsync((void **)X1_f, kferm2*sizeof(Complex_f),streams[4]);
41#endif
42#else
43 *p_f=(Complex_f *)aligned_alloc(AVX,kferm2Halo*sizeof(Complex_f));
44 *x1_f=(Complex_f *)aligned_alloc(AVX,kferm2Halo*sizeof(Complex_f));
45 *x2_f=(Complex_f *)aligned_alloc(AVX,kferm2*sizeof(Complex_f));
46
47 *r_f=(Complex_f *)aligned_alloc(AVX,kferm2*sizeof(Complex_f));
48 *X1_f=(Complex_f *)aligned_alloc(AVX,kferm2*sizeof(Complex_f));
49#endif
50 return;
51}
52
64void Q_allocate(Complex **p, Complex **x1, Complex **x2, Complex *clover[2]){
65 const char funcname[] = "Q_allocate";
66#ifdef USE_GPU
67#ifdef _DEBUG
68 cudaMallocManaged((void **)&clover[0], 6*kvol*sizeof(Complex),cudaMemAttachGlobal);
69 cudaMallocManaged((void **)&clover[1], 6*kvol*sizeof(Complex),cudaMemAttachGlobal);
70 cudaMallocManaged((void **)p, kferm2Halo*sizeof(Complex),cudaMemAttachGlobal);
71 cudaMallocManaged((void **)x1, kferm2Halo*sizeof(Complex),cudaMemAttachGlobal);
72 cudaMallocManaged((void **)x2, kferm2*sizeof(Complex),cudaMemAttachGlobal);
73#else
74 //First two have halo exchanges, so getting NCCL working is important
75 cudaMallocAsync((void **)&clover[0], 6*kvol*sizeof(Complex),streams[0]);
76 cudaMallocAsync((void **)&clover[1], 6*kvol*sizeof(Complex),streams[1]);
77 cudaMallocAsync((void **)p, kferm2Halo*sizeof(Complex),streams[2]);
78 cudaMallocAsync((void **)x1, kferm2Halo*sizeof(Complex),streams[3]);
79 cudaMallocAsync((void **)x2, kferm2*sizeof(Complex),streams[4]);
80#endif
81#else
82 clover[0]=(Complex *)aligned_alloc(AVX,6*kvol*sizeof(Complex));
83 clover[1]=(Complex *)aligned_alloc(AVX,6*kvol*sizeof(Complex));
84 *p=(Complex *)aligned_alloc(AVX,kferm2Halo*sizeof(Complex));
85 *x1=(Complex *)aligned_alloc(AVX,kferm2Halo*sizeof(Complex));
86 *x2=(Complex *)aligned_alloc(AVX,kferm2*sizeof(Complex));
87#endif
88 return;
89}
90
103void P_allocate_f(Complex_f **p_f,Complex_f **r_f, Complex_f **x1_f, Complex_f **x2_f, Complex_f **xi_f){
104#ifdef USE_GPU
105#ifdef _DEBUG
106 cudaMallocManaged((void **)p_f, kfermHalo*sizeof(Complex_f),cudaMemAttachGlobal);
107 cudaMallocManaged((void **)r_f, kferm*sizeof(Complex_f),cudaMemAttachGlobal);
108 cudaMallocManaged((void **)x1_f, kfermHalo*sizeof(Complex_f),cudaMemAttachGlobal);
109 cudaMallocManaged((void **)x2_f, kferm*sizeof(Complex_f),cudaMemAttachGlobal);
110 cudaMallocManaged((void **)xi_f, kferm*sizeof(Complex_f),cudaMemAttachGlobal);
111#else
112 cudaMalloc((void **)p_f, kfermHalo*sizeof(Complex_f));
113 cudaMalloc((void **)r_f, kferm*sizeof(Complex_f));
114 cudaMalloc((void **)x1_f, kfermHalo*sizeof(Complex_f));
115 cudaMalloc((void **)x2_f, kferm*sizeof(Complex_f));
116 cudaMalloc((void **)xi_f, kferm*sizeof(Complex_f));
117#endif
119#else
120 *p_f = aligned_alloc(AVX,kfermHalo*sizeof(Complex_f));
121 *r_f = aligned_alloc(AVX,kferm*sizeof(Complex_f));
122 *x1_f = aligned_alloc(AVX,kfermHalo*sizeof(Complex_f));
123 *x2_f = aligned_alloc(AVX,kferm*sizeof(Complex_f));
124 *xi_f = aligned_alloc(AVX,kferm*sizeof(Complex_f));
125#endif
126}
127
140void P_allocate(Complex **p, Complex **r, Complex **x1, Complex **x2,Complex *clover[2]){
141#ifdef USE_GPU
142#ifdef _DEBUG
143 cudaMallocManaged((void **)&clover[0], 6*kvol*sizeof(Complex),cudaMemAttachGlobal);
144 cudaMallocManaged((void **)&clover[1], 6*kvol*sizeof(Complex),cudaMemAttachGlobal);
145 cudaMallocManaged((void **)p, kfermHalo*sizeof(Complex), cudaMemAttachGlobal);
146 cudaMallocManaged((void **)r, kferm*sizeof(Complex), cudaMemAttachGlobal);
147 cudaMallocManaged((void **)x1, kfermHalo*sizeof(Complex), cudaMemAttachGlobal);
148 cudaMallocManaged((void **)x2, kferm*sizeof(Complex), cudaMemAttachGlobal);
149#else
150 cudaMallocAsync((void **)&clover[0], 6*kvol*sizeof(Complex),streams[0]);
151 cudaMallocAsync((void **)&clover[1], 6*kvol*sizeof(Complex),streams[1]);
152 cudaMallocAsync((void **)p, kfermHalo*sizeof(Complex),streams[2]);
153 cudaMallocAsync((void **)r, kferm*sizeof(Complex),streams[3]);
154 cudaMallocAsync((void **)x1, kfermHalo*sizeof(Complex),streams[4]);
155 cudaMallocAsync((void **)x2, kferm*sizeof(Complex),streams[5]);
156#endif
158#else
159 clover[0]=(Complex *)aligned_alloc(AVX,6*kvol*sizeof(Complex));
160 clover[1]=(Complex *)aligned_alloc(AVX,6*kvol*sizeof(Complex));
161 *p = aligned_alloc(AVX, kfermHalo*sizeof(Complex));
162 *r = aligned_alloc(AVX, kferm*sizeof(Complex));
163 *x1 = aligned_alloc(AVX, kfermHalo*sizeof(Complex));
164 *x2 = aligned_alloc(AVX, kferm*sizeof(Complex));
165#endif
166}
167
181void Q_free_f(Complex_f **p_f, Complex_f **x1_f, Complex_f **x2_f, Complex_f **r_f, Complex_f **X1_f){
182 const char funcname[] = "Q_free";
183#ifdef USE_GPU
184#ifdef _DEBUG
186 cudaFree(*x1_f);cudaFree(*x2_f); cudaFree(*p_f);
187 cudaFree(*r_f); cudaFree(*X1_f);
188#else
189 //streams match the ones that allocated them.
190 cudaFreeAsync(*p_f,streams[0]);cudaFreeAsync(*x1_f,streams[1]);cudaFreeAsync(*x2_f,streams[2]);
192 cudaFreeAsync(*r_f,streams[3]); cudaFreeAsync(*X1_f,streams[4]);
193#endif
194#else
195 free(*x1_f); free(*x2_f); free(*p_f); free(*r_f);free(*X1_f);
196#endif
197 return;
198}
199
211void Q_free(Complex **p, Complex **x1, Complex **x2, Complex *clover[2]){
212 const char funcname[] = "Qree";
213#ifdef USE_GPU
214#ifdef _DEBUG
216 cudaFree(*x1);cudaFree(*x2); cudaFree(*p);
217 cudaFree(clover[0]); cudaFree(clover[1]);
218#else
219 //streams match the ones that allocated them.
220 cudaFreeAsync(clover[0],streams[0]); cudaFreeAsync(clover[1],streams[1]);
221 cudaFreeAsync(*p,streams[2]);cudaFreeAsync(*x1,streams[3]);cudaFreeAsync(*x2,streams[4]);
223#endif
224#else
225 free(*x1);free(*x2); free(*p);
226 free(clover[0]); free(clover[1]);
227#endif
228 return;
229}
230
243void P_free_f(Complex_f **p_f,Complex_f **r_f, Complex_f **x1_f, Complex_f **x2_f, Complex_f **xi_f){
244#ifdef USE_GPU
245 cudaFree(*p_f); cudaFree(*r_f);cudaFree(*x1_f); cudaFree(*x2_f); cudaFree(*xi_f);
246#else
247 free(*p_f); free(*r_f); free(*x1_f); free(*x2_f); free(*xi_f);
248#endif
249}
250
263void P_free(Complex **p, Complex **r, Complex **x1, Complex **x2,Complex *clover[2]){
264#ifdef USE_GPU
265#ifdef _DEBUG
266 cudaFree(clover[0]); cudaFree(clover[1]);
267 cudaFree(*p);cudaFree(*r);cudaFree(*x1); cudaFree(*x2);
268#else
269 cudaFreeAsync(clover[0],streams[0]); cudaFreeAsync(clover[1],streams[1]);
270 cudaFreeAsync(*p,streams[2]);cudaFreeAsync(*r,streams[3]);cudaFreeAsync(*x1,streams[4]); cudaFreeAsync(*x2,streams[5]);
271#endif
272#else
273 free(*p); free(*r); free(*x1); free(*x2);
274 free(clover[0]); free(clover[1]);
275#endif
276}
277
278int Congradq(int na,double res,Complex *X1,Complex *r,Complex *ud[2], Complex_f *ut[2],Complex_f *clover_f[nc],
279 unsigned int *iu, unsigned int *id, Complex gamval[20], Complex_f gamval_f[20],const unsigned short gamin[16],
280 Complex *sigval, Complex_f *sigval_f,unsigned short *sigin, double *dk[2], float *dk_f[2],
281 Complex_f jqq,float akappa,float c_sw,int *itercg){
282 const char funcname[] = "Congradq";
283 int ret_val=0;
284 const double resid = res*res;
285#ifdef _DEBUGCG
286#warning "CG Debugging"
287 char *endline = "\n";
288#else
289 char *endline = "\r";
290#endif
291
294 const float d_prec=1.0f/128.0f;
297 const Complex_f fac_f = conj(jqq)*jqq*akappa*akappa;
298 //These were evaluated only in the first loop of niterx so we'll just do it outside of the loop.
299 //n suffix is numerator, d is denominator
300 alignas(16) double alphan=1;
304 //Alignment needed for cuBLAS
305 alignas(16) double betad = 1.0; alignas(8) Complex_f alphad=0; alignas(16) Complex alpha = 1;
306 //Because we're dealing with flattened arrays here we can call cblas safely without the halo
307
308 Complex_f *p_f, *x1_f, *x2_f, *r_f, *X1_f;
309 Complex *p, *x1, *x2, *clover[2];
310 Q_allocate_f(&p_f,&x1_f,&x2_f,&r_f,&X1_f);
311 Q_allocate(&p,&x1,&x2,clover);
312
313 //Instead of copying element-wise in a loop, use memcpy.
314 //Get X1 in single precision
315 //Since X1 has a halo and X1_f does not we have to do this manually
316#if (nproc>1)
317 for(unsigned short j=0;j<nc*ndirac;j++)
318 ComplexConvert(X1_f+j*kvol,X1+j*kvolHalo,kvol,true,1);
319#else
320 ComplexConvert(X1_f,X1,kferm2,true,1);
321#endif
322 ComplexConvert(r_f,r,kferm2,true,1);
323 //And clover in double
324 if(c_sw){
325 ComplexConvert(clover_f[0],clover[0],6*kvol,false,1);
326 ComplexConvert(clover_f[1],clover[1],6*kvol,false,1);
327 }
328#ifdef USE_GPU
329 //Ensure conversion is done
331 //Needs to be strided
332#if (nproc>1)
333 for(unsigned int j=0;j<nc*ndirac;j++)
334 cudaMemcpyAsync(p_f+j*kvolHalo, X1_f+j*kvol, kvol*sizeof(Complex_f),cudaMemcpyDefault,streams[j]);
336#else
337 cudaMemcpy(p_f, X1_f, kferm2*sizeof(Complex_f),cudaMemcpyDefault);
338#endif
339#else
340#pragma omp parallel for simd aligned(X1_f,r_f,X1,r:AVX)
341 for(unsigned int j=0;j<nc*ndirac;j++)
342 memcpy(p_f+j*kvolHalo, X1_f+j*kvol, kvol*sizeof(Complex_f));
343#endif
344
345 alignas(16) double betan=1;double beta_max=FLT_MAX; bool do_dp=true;
346 for(*itercg=0; *itercg<niterc; (*itercg)++){
347 if(do_dp){
348#ifdef _DEBUGCG
349 if(!rank)
350 printf("Going to double precision on iteration %d. betan %e\talpha %e.\n",
351 *itercg,betan,creal(alpha));
352#elifdef _DEBUG
353 if(!rank)
354 printf("\nGoing to double precision on iteration %d. betan %e\talpha %e.\n",
355 *itercg,betan,creal(alpha));
356#endif
357 ComplexConvert(r_f,r,kferm2,false,1);
358 ComplexConvert(p_f,p,kvol,false,nc*ndirac);
359#ifdef USE_GPU
360 //Update the residue vector, but not on the first call.
361 //TODO: Check for multi-gpu. I fear this will get messy
362 if(*itercg)
363 cuMixed_Sumto((double *)X1,(float *)X1_f,2*kferm2,dimGrid,dimBlock);
364 //Bring everything into double precision
365 //Reset X1_f to zero.
366 cudaMemset(X1_f,0,kferm2*sizeof(Complex_f));
367#else
368 //Update the residue vector, but not on the first call.
369 if(*itercg)
370#pragma omp parallel for simd collapse(2) aligned(X1,X1_f:AVX)
371 for(unsigned short j=0;j<nc*ndirac;j++)
372 for(unsigned int i=0;i<kvol;i++){
373 X1[i+j*kvolHalo]+=(Complex)X1_f[i+j*kvol];
374 }
375 memset(X1_f,0,kferm2*sizeof(Complex_f));
376#endif
378 //No need to synchronise here. The memcpy in Hdslash is blocking
379 Hdslash(x1,p,ud,iu,id,gamval,gamin,dk,akappa);
380 //Clover contribution
381 if(c_sw)
382 HbyClover(x1,p,clover,sigval,akappa,sigin,false);
383 Hdslashd(x2,x1,ud,iu,id,gamval,gamin,dk,akappa);
384 //Clover contribution
385 if(c_sw)
386 HbyClover(x2,x1,clover,sigval,akappa,sigin,true);
387#ifdef USE_GPU
389#endif
390 if(fac_f!=0){
391 alignas(16) const double fac=(double)fac_f;
392#ifdef USE_GPU
393 //Multiple ranks means we need striding
394#if (nproc>1)
395 for(unsigned short j=0;j<nc*ndirac;j++)
396 cublasZaxpy(cublas_handle,kvol,(cuDoubleComplex *)&fac,(cuDoubleComplex *)p+j*kvolHalo,1,(cuDoubleComplex *)x2+j*kvol,1);
397#else
398 //Single GPU, no halos, so make it one call to speed things up
399 cublasZaxpy(cublas_handle,kferm2,(cuDoubleComplex *)&fac,(cuDoubleComplex *)p,1,(cuDoubleComplex *)x2,1);
400#endif
401#elif defined USE_BLAS
402 for(unsigned short j=0;j<nc*ndirac;j++)
403 cblas_zaxpy(kvol, &fac, p+j*kvolHalo, 1, x2+j*kvol, 1);
404#else
405#pragma omp parallel for simd collapse(2) aligned(p,x2:AVX)
406 for(unsigned short j=0;j<nc*ndirac;j++)
407 for(unsigned int i=0; i<kvol; i++)
408 x2[i+j*kvol]+=fac*p[i+j*kvolHalo];
409#endif
410 }
411
413 if(*itercg){
414 alpha=0;
415#ifdef USE_GPU
416#if (nproc>1)
417 for(unsigned short j=0;j<nc*ndirac;j++){
418 Complex alpha_t=0;
419 cublasZdotc(cublas_handle,kvol,(cuDoubleComplex *)p+j*kvolHalo,1,(cuDoubleComplex *)x2+j*kvol,1,(cuDoubleComplex *)&alpha_t);
420 alpha+=alpha_t;
421 }
422#else
423 cublasZdotc(cublas_handle,kferm2,(cuDoubleComplex *)p,1,(cuDoubleComplex *)x2,1,(cuDoubleComplex *)&alpha);
424#endif
425#elif defined USE_BLAS
426 for(unsigned short j=0;j<nc*ndirac;j++){
427 Complex alpha_t=0;
428 cblas_zdotc_sub(kvol, p+j*kvolHalo, 1, x2+j*kvol, 1, &alpha_t);
429 alpha+=creal(alpha_t);
430 }
431#else
432#pragma omp parallel for simd collapse(2) aligned(p,x2:AVX) reduction(+:alpha)
433 for(unsigned short j=0;j<nc*ndirac;j++)
434 for(unsigned int i=0; i<kvol; i++)
435 alpha+=conj(p[i+j*kvolHalo])*x2[i+j*kvol];
436#endif
437 //For now I'll cast it into a float for the reduction. Each rank only sends and writes
438 //to the real part so this is fine
439#if (nproc>1)
440 Par_dsum((double *)&alpha);
441#endif
443 alpha=alphan/creal(alpha);
445#ifdef USE_GPU
446#if (nproc>1)
447 for(unsigned short j=0;j<nc*ndirac;j++)
448 cublasZaxpy(cublas_handle,kvol,(cuDoubleComplex *)&alpha,(cuDoubleComplex *)p+j*kvolHalo,1,(cuDoubleComplex *)X1+j*kvolHalo,1);
449#else
450 cublasZaxpy(cublas_handle,kferm2,(cuDoubleComplex *)&alpha,(cuDoubleComplex *)p,1,(cuDoubleComplex *)X1,1);
451#endif
452#elif defined USE_BLAS
453 for(unsigned short j=0;j<nc*ndirac;j++)
454 cblas_zaxpy(kvol, &alpha, p+j*kvolHalo, 1, X1+j*kvolHalo, 1);
455#else
456#pragma omp parallel for simd collapse(2) aligned(p,X1:AVX)
457 for(unsigned short j=0;j<nc*ndirac;j++)
458 for(unsigned int i=0; i<kvol; i++)
459 X1[i+j*kvolHalo]+=alpha*p[i+j*kvolHalo];
460#endif
461 }
462
463#ifdef USE_GPU
464 alignas(16) Complex alpha_m=(Complex)(-alpha);
465 cublasZaxpy(cublas_handle, kferm2,(cuDoubleComplex *)&alpha_m,(cuDoubleComplex *)x2,1,(cuDoubleComplex *)r,1);
466 alignas(16) double betan_d;
467 cublasDznrm2(cublas_handle,kferm2,(cuDoubleComplex *)r,1,&betan_d);
468 betan=betan_d*betan_d;
469#elif defined USE_BLAS
470 const Complex alpha_m = (Complex)(-alpha);
471 cblas_zaxpy(kferm2, &alpha_m, x2, 1, r, 1);
472 //Undo the negation for the BLAS routine
473 double betan_d = cblas_dznrm2(kferm2, r,1);
474 //Gotta square it to "undo" the norm
475 betan = betan_d*betan_d;
476#else
477 betan=0;
478#pragma omp parallel for simd aligned(r_f,x2_f:AVX) reduction(+:betan)
479 for(unsigned int i=0; i<kferm2; i++){
480 r[i]-=alpha*x2[i];
481 betan += conj(r[i])*r[i];
482 }
483 alignas(16) double betan_d=sqrt(betan);
484#endif
485 //And... reduce.
486#if (nproc>1)
487 Par_dsum(&betan);
488#endif
489 betan_d=sqrt(betan);
490 //Update beta_max. Mandatory for double precision.
491 beta_max=betan_d;
492#ifdef _DEBUG
493 if(!rank) printf("DP Iter(CG)=%i\tbeta_n=%e\talpha=%e\n", *itercg, betan, creal(alpha));
494 fflush(stdout);
495#endif
496 alignas(16) const Complex beta = (*itercg) ? betan/betad : 0;
497 betad=betan; alphan=betan;
498#ifdef USE_GPU
499 alpha_m=1;
500 //Strided multi-gpu
501#if (nproc>1)
502 for(unsigned short j=0;j<nc*ndirac;j++){
503 cublasZdscal(cublas_handle,kvol,(double *)&beta,(cuDoubleComplex *)p+j*kvolHalo,1);
504 cublasZaxpy(cublas_handle,kvol,(cuDoubleComplex *)&alpha_m,(cuDoubleComplex *)r+j*kvol,1,(cuDoubleComplex *)p+j*kvolHalo,1);
505 }
506 //And single GPU
507#else
508 cublasZdscal(cublas_handle,kferm2,(double *)&beta,(cuDoubleComplex *)p,1);
509 cublasZaxpy(cublas_handle,kferm2,(cuDoubleComplex *)&alpha_m,(cuDoubleComplex *)r,1,(cuDoubleComplex *)p,1);
510#endif
511#elif (defined __USE_MKL__||defined OPENBLAS||defined AMD_BLAS)
512 const Complex a = 1.0;
513 //There is cblas_?axpby in the MKL and AMD though, set a = 1 and b = \beta.
514 //If we get a small enough \beta_n before hitting the iteration cap we break
515 for(unsigned short j=0;j<nc*ndirac;j++)
516 cblas_zaxpby(kvol, &a, r+j*kvol, 1, &beta, p+j*kvolHalo, 1);
517#elifdef USE_BLAS
518 const Complex a = 1.0;
519 for(unsigned short j=0;j<nc*ndirac;j++){
520 cblas_zscal(kvol,&beta,p+j*kvolHalo,1);
521 cblas_zaxpy(kvol,&a,r+j*kvol,1,p+j*kvolHalo,1);
522 }
523#else
524#pragma omp parallel for simd collapse(2) aligned(r,p:AVX)
525 for(unsigned short j=0;j<nc*ndirac;j++)
526 for(unsigned int i=0; i<kvol; i++)
527 p[i+j*kvolHalo]=r[i+j*kvol]+beta*p[i+j*kvolHalo];
528#endif
529 ComplexConvert(p_f,p,kvol,true,nc*ndirac);
530 ComplexConvert(r_f,r,kferm2,true,1);
531#ifdef _DEBUGCG
532 if(!rank) printf("Double precision. Iter(CG)=%i\tbeta_n=%e\talpha=%e\n", *itercg, betan, creal(alpha));
533 fflush(stdout);
534#endif
535 if(betan<resid){
536 (*itercg)++;
537#ifdef _DEBUG
538 if(!rank) printf("\nIter(CG)=%i\tResidue: %e\tTolerance: %e\n", *itercg, betan, resid);
539#endif
540 ret_val=0; break;
541 }
542 else
543 do_dp=false;
544 }
545 else{
547 //No need to synchronise here. The memcpy in Hdslash is blocking
548 Hdslash_f(x1_f,p_f,ut,iu,id,gamval_f,gamin,dk_f,akappa);
549 //Clover contribution
550 if(c_sw)
551 HbyClover_f(x1_f,p_f,clover_f,sigval_f,akappa,sigin,false);
552 Hdslashd_f(x2_f,x1_f,ut,iu,id,gamval_f,gamin,dk_f,akappa);
553 //Clover contribution
554 if(c_sw)
555 HbyClover_f(x2_f,x1_f,clover_f,sigval_f,akappa,sigin,true);
556#ifdef USE_GPU
558#endif
560 //No point adding zero a couple of hundred times if the diquark source is zero
561 if(fac_f!=0){
562#ifdef USE_GPU
563 //Strided multi-gpu
564#if (nproc>1)
565 for(unsigned short j=0;j<nc*ndirac;j++)
566 cublasCaxpy(cublas_handle,kvol,(cuComplex *)&fac_f,(cuComplex *)p_f+j*kvolHalo,1,(cuComplex *)x2_f+j*kvol,1);
567 //Single GPU
568#else
569 cublasCaxpy(cublas_handle,kferm2,(cuComplex *)&fac_f,(cuComplex *)p_f,1,(cuComplex *)x2_f,1);
570#endif
571#elif defined USE_BLAS
572 for(unsigned short j=0;j<nc*ndirac;j++)
573 cblas_caxpy(kvol, &fac_f, p_f+j*kvolHalo, 1, x2_f+j*kvol, 1);
574#else
575#pragma omp parallel for simd collapse(2) aligned(p_f,x2_f:AVX)
576 for(unsigned short j=0;j<nc*ndirac;j++)
577 for(unsigned int i=0; i<kvol; i++)
578 x2_f[i+j*kvol]+=fac_f*p_f[i+j*kvolHalo];
579#endif
580 }
583 if(*itercg){
584 alphad=0;
585#ifdef USE_GPU
586#if (nproc>1)
587 for(unsigned short j=0;j<nc*ndirac;j++){
588 Complex alpha_t=0;
589 cublasCdotc(cublas_handle,kvol,(cuComplex *)p_f+j*kvolHalo,1,(cuComplex *)x2_f+j*kvol,1,(cuComplex *)&alpha_t);
590 alphad+=alpha_t;
591 }
592#else
593 cublasCdotc(cublas_handle,kferm2,(cuComplex *)p_f,1,(cuComplex *)x2_f,1,(cuComplex *)&alphad);
594#endif
595#elif defined USE_BLAS
596 for(unsigned short j=0;j<nc*ndirac;j++){
597 Complex_f alpha_t=0;
598 cblas_cdotc_sub(kvol, p_f+j*kvolHalo, 1, x2_f+j*kvol, 1, &alpha_t);
599 alphad+=creal(alpha_t);
600 }
601#else
602#pragma omp parallel for simd aligned(p_f,x2_f:AVX) reduction(alphad:+)
603 for(unsigned short j=0;j<nc*ndirac;j++)
604 for(unsigned int i=0; i<kvol; i++)
605 alphad+=conj(p_f[i+j*kvolHalo])*x2_f[i+j*kvol];
606#endif
607 //For now I'll cast it into a float for the reduction. Each rank only sends and writes
608 //to the real part so this is fine
609#if (nproc>1)
610 Par_fsum((float *)&alphad);
611#endif
613 alpha=alphan/creal(alphad);
615#ifdef USE_GPU
616 alignas(8) Complex_f alpha_f = (Complex_f)alpha;
617#if (nproc>1)
618 for(unsigned short j=0;j<nc*ndirac;j++)
619 cublasCaxpy(cublas_handle,kvol,(cuComplex *)&alpha_f,(cuComplex *)p_f+j*kvolHalo,1,(cuComplex *)X1_f+j*kvol,1);
620#else
621 cublasCaxpy(cublas_handle,kferm2,(cuComplex *)&alpha_f,(cuComplex *)p_f,1,(cuComplex *)X1_f,1);
622#endif
623#elif defined USE_BLAS
624 Complex_f alpha_f = (Complex_f)alpha;
625 for(unsigned short j=0;j<nc*ndirac;j++)
626 cblas_caxpy(kvol, &alpha_f, p_f+j*kvolHalo, 1, X1_f+j*kvol, 1);
627#else
628#pragma omp parallel for simd collapse(2) aligned(X1_f,p_f:AVX)
629 for(unsigned short j=0;j<nc*ndirac;j++)
630 for(unsigned int i=0; i<kvol; i++)
631 X1_f[i+j*kvol]+=alpha*p_f[i+j*kvolHalo];
632#endif
633 }
635 // And no Halos here so nice and easy
636#ifdef USE_GPU
637 alignas(8) __managed__ Complex_f alpha_m=(Complex_f)(-alpha);
638 cublasCaxpy(cublas_handle, kferm2,(cuComplex *)&alpha_m,(cuComplex *)x2_f,1,(cuComplex *)r_f,1);
639 alignas(8) float betan_f;
640 cublasScnrm2(cublas_handle,kferm2,(cuComplex *)r_f,1,&betan_f);
641 betan = betan_f*betan_f;
642#elif defined USE_BLAS
643 const Complex_f alpha_m = (Complex_f)(-alpha);
644 cblas_caxpy(kferm2, &alpha_m, x2_f, 1, r_f, 1);
645 //Undo the negation for the BLAS routine
646 float betan_f = cblas_scnrm2(kferm2, r_f,1);
647 //Gotta square it to "undo" the norm
648 betan = betan_f*betan_f;
649#else
650 betan=0;
651#pragma omp parallel for simd aligned(r_f,x2_f:AVX) reduction(+:betan)
652 for(unsigned int i=0; i<kferm2; i++){
653 r_f[i]-=alpha*x2_f[i];
654 betan += conj(r_f[i])*r_f[i];
655 }
656 alignas(8) float betan_f=sqrt(betan);
657#endif
658 //And... reduce.
659#if (nproc>1)
660 Par_dsum(&betan);
661#endif
662 betan_f=sqrt(betan);
663 //Update beta_max if needed. On paper congrad is monotonically decreasing
664 beta_max = (betan_f>beta_max) ?betan_f : beta_max;
665
666#ifdef _DEBUG
667 if(!rank) printf("Iter(CG)=%i\tbeta_n=%e\talpha=%e%s", *itercg, betan, creal(alpha),endline);
668 fflush(stdout);
669#endif
670 if(betan_f<beta_max*d_prec){
671#ifdef _DEBUG
672 if(!rank)
673 printf("\nResidue %e is less than %e times %e.%s",betan_f,d_prec,beta_max,endline);
674#endif
675 do_dp=true;
676 }
677 else if(betan<resid){
678#ifdef _DEBUG
679 if(!rank)
680 printf("\nBetan %e is less than target residue %e.\n",betan,resid);
681#endif
682 do_dp=true;
683 //break;
684 }
685 else if(*itercg==niterc-1){
686 if(!rank) fprintf(stderr, "\nWarning %i in %s: Exceeded iteration limit %i beta_n=%e\n",\
687 ITERLIM, funcname, *itercg, betan);
688 ret_val=ITERLIM; break;
689 }
690 //Here we evaluate beta=(r_{k+1}.r_{k+1})/(r_k.r_k) and then shuffle our indices down the line.
691 //On the first iteration we define beta to be zero.
692 //Note that beta below is not the global beta and scoping is used to avoid conflict between them
693 Complex beta = (*itercg) ? betan/betad : 0;
694 betad=betan; alphan=betan;
695 //BLAS for p=r+\betap doesn't exist in standard BLAS. This is NOT an axpy case as we're multiplying y by
696 //\beta instead of x.
697#ifdef USE_GPU
698 alignas(8) Complex_f beta_f=(Complex_f)beta;
699 alpha_m = 1.0;
700#if (nproc>1)
701 for(unsigned short j=0;j<nc*ndirac;j++){
702 cublasCscal(cublas_handle,kvol,(cuComplex *)&beta_f,(cuComplex *)p_f+j*kvolHalo,1);
703 cublasCaxpy(cublas_handle,kvol,(cuComplex *)&alpha_m,(cuComplex *)r_f+j*kvolHalo,1,(cuComplex *)p_f+j*kvolHalo,1);
704 }
705#else
706 cublasCscal(cublas_handle,kferm2,(cuComplex *)&beta_f,(cuComplex *)p_f,1);
707 cublasCaxpy(cublas_handle,kferm2,(cuComplex *)&alpha_m,(cuComplex *)r_f,1,(cuComplex *)p_f,1);
708#endif
709#elif (defined __USE_MKL__||defined OPENBLAS||defined AMD_BLAS)
710 Complex_f a = 1.0;
711 Complex_f beta_f=(Complex_f)beta;
712 //There is cblas_?axpby in the MKL and AMD though, set a = 1 and b = \beta.
713 //If we get a small enough \beta_n before hitting the iteration cap we break
714 for(unsigned short j=0;j<nc*ndirac;j++)
715 cblas_caxpby(kvol, &a, r_f+j*kvol, 1, &beta_f, p_f+j*kvolHalo, 1);
716#elif defined USE_BLAS
717 Complex_f beta_f=(Complex_f)beta;
718 Complex_f a = 1.0;
719 for(unsigned short j=0;j<nc*ndirac;j++){
720 cblas_cscal(kvol,&beta_f,p_f+j*kvolHalo,1);
721 cblas_caxpy(kvol,&a,r_f+j*kvol,1,p_f+j*kvolHalo,1);
722 }
723#else
724#pragma omp parallel for simd collapse(2) aligned(p_f,r_f:AVX)
725 for(unsigned short j=0;j<nc*ndirac;j++)
726 for(unsigned int i=0; i<kvol; i++)
727 p_f[i+j*kvolHalo]=r_f[i+j*kvol]+beta*p_f[i+j*kvolHalo];
728#endif
729 }
730 }
731 Q_free_f(&p_f,&x1_f,&x2_f,&r_f,&X1_f);
732 Q_free(&p,&x1,&x2,clover);
733 return ret_val;
734}
735int Congradp(int na, double res, Complex *Phi, Complex *xi, Complex *ud[2], Complex_f *ut[2], Complex_f *clover_f[nc],
736 unsigned int *iu, unsigned int *id, Complex gamval[20], Complex_f gamval_f[20], const unsigned short gamin[16],
737 Complex *sigval, Complex_f *sigval_f,unsigned short *sigin, double *dk[2], float *dk_f[2],
738 Complex_f jqq,float akappa,float c_sw,int *itercg){
739 const char funcname[] = "Congradp";
740 //Return value
741 int ret_val=0;
742 const double resid = res*res;
743#ifdef _DEBUGCG
744#warning "CG Debugging"
745 char *endline = "\n";
746#else
747 char *endline = "\r";
748#endif
749
752 const float d_prec=1.0f/128.0f;
753
754 //These were evaluated only in the first loop of niterx so we'll just do it outside of the loop.
755 alignas(8) double alphan=1.0;
756 //These alpha and beta terms should be double, but that causes issues with BLAS. Instead we declare
757 //them Complex and work with the real part (especially for \alpha_d)
758 //Give initial values Will be overwritten if niterx>0
759 alignas(8) double betad = 1.0; alignas(8) Complex_f alphad=0; alignas(16)Complex alpha = 1; //Alignment needed for cuBLAS
760
761 Complex_f *p_f, *r_f, *x1_f, *x2_f, *xi_f;
762 Complex *p, *r, *x1, *x2, *clover[nc];
763 P_allocate_f(&p_f,&r_f,&x1_f,&x2_f,&xi_f);
764 P_allocate(&p, &r, &x1, &x2,clover);
765
766 ComplexConvert(r_f,Phi+na*kferm,kferm,true,1);
767 ComplexConvert(xi_f,xi,kferm,true,1);
768 if(c_sw){
769 ComplexConvert(clover_f[0],clover[0],6*kvol,false,1);
770 ComplexConvert(clover_f[1],clover[1],6*kvol,false,1);
771 }
772 //Instead of copying element-wise in a loop, use memcpy.
773#ifdef USE_GPU
774 //Get r in single precision
775 //Get xi in single precision
776 cudaMemcpy(r,Phi+na*kferm,kferm*sizeof(Complex),cudaMemcpyDefault);
777#if (nproc>1)//strided memcpy
778 for(unsigned short j=0;j<nc*ngorkov;j++){
779 cudaMemcpyAsync(p+j*kvolHalo,xi+j*kvol,kvol*sizeof(Complex),cudaMemcpyDefault,streams[j]);
780 cudaMemcpyAsync(p_f+j*kvolHalo,xi_f+j*kvol,kvol*sizeof(Complex_f),cudaMemcpyDefault,streams[j]);
781 }
782#else
783 cudaMemcpyAsync(p,xi,kferm*sizeof(Complex),cudaMemcpyDefault,streams[0]);
784 cudaMemcpyAsync(p_f,xi_f,kferm*sizeof(Complex_f),cudaMemcpyDefault,streams[1]);
785#endif
787#else
788 memcpy(r,Phi+na*kferm,kferm*sizeof(Complex));
789 //These strided loops for MPI halos are a total pain in the arse...
790 for(unsigned short j=0;j<nc*ngorkov;j++){
791 memcpy(p+j*kvolHalo,xi+j*kvol,kvol*sizeof(Complex));
792 memcpy(p_f+j*kvolHalo,xi_f+j*kvol,kvol*sizeof(Complex_f));
793 }
794#endif
795
796 double betan=1;double beta_max=FLT_MAX; bool do_dp=true;
797 for((*itercg)=0; (*itercg)<niterc; (*itercg)++){
798 //Don't overwrite on first run.
799 if(do_dp){
800#ifdef _DEBUGCG
801 if(!rank)
802 printf("Going to double precision on iteration %d. %sbetan %e\talpha %e. %s",
803 *itercg,endline,betan,creal(alpha),endline);
804#elifdef _DEBUG
805 if(!rank)
806 printf("\nGoing to double precision on iteration %d. %sbetan %e\talpha %e.\n",
807 *itercg,endline,betan,creal(alpha));
808#endif
809 ComplexConvert(r_f,r,kferm,false,1);
810 //TODO: Banking on converting the halo too being faster than multiple launches
811 ComplexConvert(p_f,p,kvol,false,nc*ngorkov);
812#ifdef USE_GPU
813 //Update the residue vector, but not on the first call.
814 if(*itercg)
815 cuMixed_Sumto((double *)xi,(float *)xi_f,2*kferm,dimGrid,dimBlock);
816 //Bring everything into double precision
817 //Reset xi_f to zero.
819 cudaMemsetAsync(xi_f,0,kferm*sizeof(Complex_f),streams[4]);
820#else
821 //Update the residue vector, but not on the first call.
822 if(*itercg)
823#pragma omp parallel for simd aligned(xi,xi_f:AVX)
824 for(unsigned int i=0;i<kferm;i++)
825 xi[i]+=(Complex)xi_f[i];
826 memset(xi_f,0,kferm*sizeof(Complex_f));
827#endif
829 //No need to synchronise here. The memcpy in Dslash is blocking
830 Dslash(x1,p,ud,iu,id,gamval,gamin,dk,jqq,akappa);
831 if(c_sw)
832 ByClover(x1,p,clover,sigval,akappa,sigin,false);
833 Dslashd(x2,x1,ud,iu,id,gamval,gamin,dk,jqq,akappa);
834 if(c_sw)
835 ByClover(x2,x1,clover,sigval,akappa,sigin,true);
836#ifdef USE_GPU
838#endif
839
841 if(*itercg){
842 alpha=0;
843#ifdef USE_GPU
844#if (nproc>1)//Strided
845 for(unsigned short j=0;j<nc*ngorkov;j++){
846 alignas(16) Complex alpha_t=0;
847 cublasZdotc(cublas_handle,kvol,(cuDoubleComplex *)p+j*kvolHalo,1,(cuDoubleComplex *)x2+j*kvol,1,(cuDoubleComplex *)&alpha_t);
848 alpha+=alpha_t
849 }
850#else
851 cublasZdotc(cublas_handle,kferm,(cuDoubleComplex *)p,1,(cuDoubleComplex *)x2,1,(cuDoubleComplex *)&alpha);
852#endif
853#elif defined USE_BLAS
854 for(unsigned short j=0;j<nc*ngorkov;j++){
855 Complex alpha_t=0;
856 cblas_zdotc_sub(kvol, p+j*kvolHalo, 1, x2+j*kvol, 1, &alpha_t);
857 alpha+=alpha_t;
858 }
859#else
860#pragma omp parallel for simd collapse(2) aligned(p,x2:AVX) reduction(+:alpha)
861 for(unsigned short j=0;j<nc*ngorkov;j++)
862 for(unsigned int i=0; i<kvol; i++)
863 alpha+=conj(p[i+j*kvolHalo])*x2[i+j*kvol];
864#endif
865 //For now I'll cast it into a float for the reduction. Each rank only sends and writes
866 //to the real part so this is fine
867#if (nproc>1)
868 Par_dsum((double *)&alpha);
869#endif
871 alpha=alphan/creal(alpha);
873#ifdef USE_GPU
874#if (nproc>1)
875 for(unsigned short j=0;j<nc*ngorkov;j++)
876 cublasZaxpy(cublas_handle,kvol,(cuDoubleComplex *)&alpha,(cuDoubleComplex *)p+j*kvolHalo,1,(cuDoubleComplex *)xi+j*kvol,1);
877#else
878 cublasZaxpy(cublas_handle,kferm,(cuDoubleComplex *)&alpha,(cuDoubleComplex *)p,1,(cuDoubleComplex *)xi,1);
879#endif
880#elif defined USE_BLAS
881 for(unsigned short j=0;j<nc*ngorkov;j++)
882 cblas_zaxpy(kvol, &alpha, p+j*kvolHalo, 1, xi+j*kvol, 1);
883#else
884#pragma omp parallel for simd collapse(2) aligned(p,xi:AVX)
885 for(unsigned short j=0;j<nc*ngorkov;j++)
886 for(unsigned int i=0; i<kvol; i++)
887 xi[i+j*kvol]+=alpha*p[i+j*kvolHalo];
888#endif
889 }
890
891#ifdef USE_GPU
892 Complex alpha_m=(Complex)(-alpha);
893 cublasZaxpy(cublas_handle, kferm,(cuDoubleComplex *)&alpha_m,(cuDoubleComplex *)x2,1,(cuDoubleComplex *)r,1);
894 double betan_d;
895 cublasDznrm2(cublas_handle,kferm,(cuDoubleComplex *)r,1,&betan_d);
896 betan=betan_d*betan_d;
897#elif defined USE_BLAS
898 const Complex alpha_m = (Complex)(-alpha);
899 cblas_zaxpy(kferm, &alpha_m, x2, 1, r, 1);
900 //Undo the negation for the BLAS routine
901 double betan_d = cblas_dznrm2(kferm, r,1);
902 //Gotta square it to "undo" the norm
903 betan = betan_d*betan_d;
904#else
905 betan=0;
906#pragma omp parallel for simd aligned(r,x2:AVX) reduction(+:betan)
907 for(unsigned int i=0; i<kferm; i++){
908 r[i]-=alpha*x2[i];
909 betan += conj(r[i])*r[i];
910 }
911 double betan_d=sqrt(betan);
912#endif
913 //And... reduce.
914#if (nproc>1)
915 Par_dsum(&betan);
916#endif
917 betan_d=sqrt(betan);
918 //Update beta_max. Mandatory for double precision.
919 beta_max=betan_d;
920#ifdef _DEBUG
921 if(! rank) printf("DP Iter(CG)=%i\tbeta_n=%e\talpha=%e\n", *itercg, betan, creal(alpha));
922 fflush(stdout);
923#endif
924 alignas(16) const Complex beta = (*itercg) ? betan/betad : 0;
925 betad=betan; alphan=betan;
926#ifdef USE_GPU
927 alpha_m=1;
928#if (nproc>1)
929 for(unsigned short j=0;j<nc*ngorkov;j++){
930 cublasZdscal(cublas_handle,kvol,(double *)&beta,(cuDoubleComplex *)p+j*kvolHalo,1);
931 cublasZaxpy(cublas_handle,kvol,(cuDoubleComplex *)&alpha_m,(cuDoubleComplex *)r+j*kvol,1,(cuDoubleComplex *)p+j*kvolHalo,1);
932 }
933#else
934 cublasZdscal(cublas_handle,kferm,(double *)&beta,(cuDoubleComplex *)p,1);
935 cublasZaxpy(cublas_handle,kferm,(cuDoubleComplex *)&alpha_m,(cuDoubleComplex *)r,1,(cuDoubleComplex *)p,1);
936#endif
937#elif (defined __USE_MKL__||defined OPENBLAS||defined AMD_BLAS)
938 const Complex a = 1.0;
939 //There is cblas_? axpby in the MKL and AMD though, set a = 1 and b = \beta.
940 //If we get a small enough \beta_n before hitting the iteration cap we break
941 for(unsigned short j=0;j<nc*ngorkov;j++)
942 cblas_zaxpby(kvol, &a, r+j*kvol, 1, &beta, p+j*kvolHalo, 1);
943#elif defined USE_BLAS
944 const Complex a = 1.0;
945 for(unsigned short j=0;j<nc*ngorkov;j++){
946 cblas_zscal(kvol,&beta,p+j*kvolHalo,1);
947 cblas_zaxpy(kvol,&a,r+j*kvol,1,p+j*kvolHalo,1);
948 }
949#else
950#pragma omp parallel for simd collapse(2) aligned(r,p:AVX)
951 for(unsigned short j=0;j<nc*ngorkov;j++)
952 for(unsigned int i=0; i<kvol; i++)
953 p[i+j*kvolHalo]=r[i+j*kvol]+beta*p[i+j*kvolHalo];
954#endif
955 ComplexConvert(p_f,p,kvol,true,nc*ngorkov);
956 ComplexConvert(r_f,r,kferm,true,1);
957#ifdef _DEBUGCG
958 if(! rank) printf("Double precision. Iter(CG)=%i\tbeta_n=%e\talpha=%e\n", *itercg, betan, creal(alpha));
959 fflush(stdout);
960#endif
961 if(betan<resid){
962 (*itercg)++;
963#ifdef _DEBUG
964 if(!rank) printf("\nIter(CG)=%i\tResidue: %e\tTolerance: %e\n", *itercg, betan, resid);
965#endif
966 ret_val=0; break;
967 }
968 else
969 do_dp=false;
970 }
971 else{
972 //x2=(M^\dagger)x1=(M^\dagger)Mp
973 Dslash_f(x1_f,p_f,ut,iu,id,gamval_f,gamin,dk_f,jqq,akappa);
974 if(c_sw)
975 ByClover_f(x1_f,p_f,clover_f,sigval_f,akappa,sigin,false);
976 Dslashd_f(x2_f,x1_f,ut,iu,id,gamval_f,gamin,dk_f,jqq,akappa);
977 if(c_sw)
978 ByClover_f(x2_f,x1_f,clover_f,sigval_f,akappa,sigin,true);
979#ifdef USE_GPU
981#endif
982 //We can't evaluate \alpha on the first niterx because we need to get \beta_n.
983 if(*itercg){
984 //x*.x
985 alphad=0;
986#ifdef USE_GPU
987#if(nproc>1)//strided
988 for(unsigned short j=0;j<nc*ngorkov;j++){
989 alignas(8) Complex_f alpha_t=0;
990 cublasCdotc(cublas_handle,kvol,(cuComplex *)p_f+j*kvolHalo,1,(cuComplex *)x2_f+j*kvol,1,(cuComplex *)&alpha_t);
991 alphad+=alpha_t;
992 }
993#else
994 cublasCdotc(cublas_handle,kferm,(cuComplex *)p_f,1,(cuComplex *)x2_f,1,(cuComplex *)&alphad);
995#endif
996#elifdef USE_BLAS
997 for(unsigned short j=0;j<nc*ngorkov;j++){
998 alignas(8) Complex_f alpha_t=0;
999 cblas_cdotc_sub(kvol,p_f+j*kvolHalo,1,x2_f+j*kvol,1,&alpha_t);
1000 alphad+=alpha_t;
1001 }
1002#else
1003#pragma omp parallel for simd collapse(2) aligned(p_f,x2_f:AVX) reduction(+:alphad)
1004 for(unsigned short j=0;j<nc*ngorkov;j++)
1005 for(unsigned int i = 0; i<kvol; i++)
1006 alphad+=conj(p_f[i+j*kvolHalo])*x2_f[i+j*kvol];
1007#endif
1008#if (nproc>1)
1009 Par_fsum((float *)&alphad);
1010#endif
1011 //alpha=(r.r)/p(M^\dagger)Mp
1012 alpha=alphan/creal(alphad);
1013 // Complex_f alpha_f = (Complex_f)alpha;
1014 //x+\alpha p
1015#ifdef USE_BLAS
1016 alignas(8) Complex_f alpha_f=(float)alpha;
1017#ifdef USE_GPU
1018#if (nproc>1) //strided
1019 for(unsigned short j=0;j<nc*ngorkov;j++)
1020 cublasCaxpy(cublas_handle,kvol,(cuComplex*) &alpha_f,(cuComplex*) p_f+j*kvolHalo,1,(cuComplex*) xi_f+j*kvol,1);
1021#else
1022 cublasCaxpy(cublas_handle,kferm,(cuComplex*) &alpha_f,(cuComplex*) p_f,1,(cuComplex*) xi_f,1);
1023#endif
1024#else
1025 for(unsigned short j=0;j<nc*ngorkov;j++)
1026 cblas_caxpy(kvol, (Complex_f*)&alpha_f,(Complex_f*)p_f+j*kvolHalo, 1, (Complex_f*)xi_f+j*kvol, 1);
1027#endif
1028#else
1029#pragma omp parallel for simd collapse(2) aligned(xi_f,p_f:AVX)
1030 for(unsigned short j=0;j<nc*ngorkov;j++)
1031 for(unsigned int i = 0; i<kvol; i++)
1032 xi_f[i+j*kvol]+=alpha*p_f[i+j*kvolHalo];
1033#endif
1034 }
1035
1036 //r=\alpha(M^\dagger)Mp and \beta_n=r*.r
1037 alignas(8) float betan_f=0;
1038#if defined USE_BLAS
1039 alignas(16) Complex_f alpha_m=(Complex_f)(-alpha);
1040#ifdef USE_GPU
1041 cublasCaxpy(cublas_handle,kferm, (cuComplex *)&alpha_m,(cuComplex *) x2_f, 1,(cuComplex *) r_f, 1);
1042 //cudaDeviceSynchronise();
1043 //r*.r
1044 cublasScnrm2(cublas_handle,kferm,(cuComplex *) r_f,1,(float *)&betan_f);
1045#else
1046 cblas_caxpy(kferm,(Complex_f*) &alpha_m,(Complex_f*) x2_f, 1,(Complex_f*) r_f, 1);
1047 //r*.r
1048 betan_f = cblas_scnrm2(kferm, (Complex_f*)r_f,1);
1049#endif
1050 //Gotta square it to "undo" the norm
1051 betan=betan_f*betan_f;
1052#else
1053 //Just like Congradq, this loop could be unrolled but will need a reduction to deal with the betan
1054 //addition.
1055 betan = 0;
1056 //If we get a small enough \beta_n before hitting the iteration cap we break
1057#pragma omp parallel for simd aligned(x2_f,r_f:AVX) reduction(+:betan)
1058 for(unsigned int i = 0; i<kferm;i++){
1059 r_f[i]-=alpha*x2_f[i];
1060 betan+=conj(r_f[i])*r_f[i];
1061 }
1062 betan_f=sqrt(betan);
1063#endif
1064 //This is basically just congradq at the end. Check there for comments
1065#if (nproc>1)
1066 Par_dsum(&betan);
1067#endif
1068 //Update beta_max if needed. On paper congrad is monotonically decreasing
1069 betan_f=sqrt(betan);
1070 beta_max = (betan_f>beta_max) ?betan_f : beta_max;
1071#ifdef _DEBUG
1072 if(!rank) printf("Iter (CG) = %i beta_n= %e alpha= %e%s", *itercg, betan, creal(alpha),endline);
1073#endif
1074 if(betan_f<beta_max*d_prec){
1075#ifdef _DEBUG
1076 if(!rank)
1077 printf("Residue %e is less than %e times %e. %s",betan_f,d_prec,beta_max,endline);
1078#endif
1079 do_dp=true;
1080 }
1081 else if(betan<resid){
1082 //Started counting from zero so add one to make it accurate
1083 (*itercg)++;
1084#ifdef _DEBUG
1085 if(!rank) printf("\nIter (CG) = %i resid = %e toler = %e\n", *itercg, betan, resid);
1086#endif
1087 //Final iteration should be in double precision.
1088 do_dp=true;
1089 }
1090 else if(*itercg==niterc-1){
1091 if(!rank) fprintf(stderr, "Warning %i in %s: Exceeded iteration limit %i beta_n=%e\n",
1092 ITERLIM, funcname, niterc, betan);
1093 ret_val=ITERLIM; break;
1094 }
1095 //Note that beta below is not the global beta and scoping is used to avoid conflict between them
1096 alignas(8) Complex beta = (*itercg) ? betan/betad : 0;
1097 betad=betan; alphan=betan;
1098 //BLAS for p=r+\betap doesn't exist in standard BLAS. This is NOT an axpy case as we're multiplying y by
1099 //\beta instead of x.
1100 //There is cblas_zaxpby in the MKL though, set a = 1 and b = \beta.
1101#ifdef USE_BLAS
1102 alignas(8) Complex_f beta_f = (Complex_f)beta;
1103 alignas(8) Complex_f a = 1.0;
1104#ifdef USE_GPU
1105#if (nproc>1) //strided
1106 for(unsigned short j=0;j<nc*ngorkov;j++){
1107 cublasCscal(cublas_handle,kvol,(cuComplex *)&beta_f,(cuComplex *)p_f+j*kvolHalo,1);
1108 cublasCaxpy(cublas_handle,kvol,(cuComplex *)&a,(cuComplex *)r_f+j*kvol,1,(cuComplex *)p_f+j*kvolHalo,1);
1109 }
1110#else
1111 cublasCscal(cublas_handle,kferm,(cuComplex *)&beta_f,(cuComplex *)p_f,1);
1112 cublasCaxpy(cublas_handle,kferm,(cuComplex *)&a,(cuComplex *)r_f,1,(cuComplex *)p_f,1);
1113#endif
1115#elif (defined __USE_MKL__||defined OPENBLAS||defined AMD_BLAS)
1116 for(unsigned short j=0;j<nc*ngorkov;j++)
1117 cblas_caxpby(kvol, &a, r_f+j*kvol, 1, &beta_f, p_f+j*kvolHalo, 1);
1118#else
1119 for(unsigned short j=0;j<nc*ngorkov;j++){
1120 cblas_cscal(kvol,&beta_f,p_f+j*kvolHalo,1);
1121 cblas_caxpy(kvol,&a,r_f+j*kvol,1,p_f+j*kvolHalo,1);
1122 }
1123#endif
1124#else
1125#pragma omp parallel for simd aligned(r_f,p_f:AVX)
1126 for(unsigned short j=0;j<nc*ngorkov;j++)
1127 for(unsigned int i=0; i<kvol; i++)
1128 p_f[i+j*kvolHalo]=r_f[i+j*kvol]+beta*p_f[i+j*kvolHalo];
1129#endif
1130 }
1131 }
1132#ifdef USE_GPU
1134#endif
1135 P_free_f(&p_f,&r_f,&x1_f,&x2_f,&xi_f);
1136 P_free(&p,&r,&x1,&x2,clover);
1137 return ret_val;
1138}
Routines needed for Clover improved wilson fermions.
#define ITERLIM
Exceeded max number of iterations.
Definition errorcodes.h:137
void HbyClover(Complex *phi, Complex *r, Complex *clover[2], Complex *sigval, const float akappa, unsigned short *sigin, bool dag)
Clover analogue of the Dslash operation. This version acts on all flavours similar to Dslash and Dsla...
Definition clover.c:289
void ByClover(Complex *phi, Complex *r, Complex *clover[2], Complex *sigval, const float akappa, unsigned short *sigin, bool dag)
Clover analogue of the Dslash operation. This version acts on all flavours similar to Dslash and Dsla...
Definition clover.c:243
void ByClover_f(Complex_f *phi, Complex_f *r, Complex_f *clover[2], Complex_f *sigval, const float akappa, unsigned short *sigin, bool dag)
Clover analogue of the Dslash operation. This version acts on all flavours similar to Dslash and Dsla...
Definition clover.c:336
void HbyClover_f(Complex_f *phi, Complex_f *r, Complex_f *clover[2], Complex_f *sigval, const float akappa, unsigned short *sigin, bool dag)
Clover analogue of the Dslash operation. This version acts on all flavours similar to Dslash and Dsla...
Definition clover.c:382
int Dslash_f(Complex_f *phi, Complex_f *r, Complex_f *ut[nc], unsigned int *iu, unsigned int *id, Complex_f gamval[20], const unsigned short gamin[16], float *dk[nc], Complex_f jqq, float akappa)
Evaluates in single precision.
Definition matrices.c:425
int Dslashd_f(Complex_f *phi, Complex_f *r, Complex_f *ut[nc], unsigned int *iu, unsigned int *id, Complex_f gamval[20], const unsigned short gamin[16], float *dk[nc], Complex_f jqq, float akappa)
Evaluates in single precision.
Definition matrices.c:544
int Hdslashd_f(Complex_f *phi, Complex_f *r, Complex_f *ut[nc], unsigned int *iu, unsigned int *id, Complex_f gamval[20], const unsigned short gamin[16], float *dk[nc], float akappa)
Evaluates in single precision.
Definition matrices.c:746
int Hdslashd(Complex *phi, Complex *r, Complex *ut[nc], unsigned int *iu, unsigned int *id, Complex gamval[20], const unsigned short gamin[16], double *dk[nc], float akappa)
Evaluates in double precision.
Definition matrices.c:338
int Hdslash(Complex *phi, Complex *r, Complex *ut[nc], unsigned int *iu, unsigned int *id, Complex gamval[20], const unsigned short gamin[16], double *dk[nc], float akappa)
Evaluates in double precision.
Definition matrices.c:254
int Dslashd(Complex *phi, Complex *r, Complex *ut[nc], unsigned int *iu, unsigned int *id, Complex gamval[20], const unsigned short gamin[16], double *dk[nc], Complex_f jqq, float akappa)
Evaluates in double precision.
Definition matrices.c:135
int Hdslash_f(Complex_f *phi, Complex_f *r, Complex_f *ut[nc], unsigned int *iu, unsigned int *id, Complex_f gamval[20], const unsigned short gamin[16], float *dk[nc], float akappa)
Evaluates in single precision.
Definition matrices.c:663
int Dslash(Complex *phi, Complex *r, Complex *ut[nc], unsigned int *iu, unsigned int *id, Complex gamval[20], const unsigned short gamin[16], double *dk[nc], Complex_f jqq, float akappa)
Evaluates in double precision.
Definition matrices.c:16
void Q_allocate_f(Complex_f **p_f, Complex_f **x1_f, Complex_f **x2_f, Complex_f **r_f, Complex_f **X1_f)
Allocates memory needed for Congradq. Just to improve readability Note that since C does not modify i...
Definition congrad.c:23
void Q_allocate(Complex **p, Complex **x1, Complex **x2, Complex *clover[2])
Allocates double precision memory needed for Congradq. Just to improve readability Note that since C ...
Definition congrad.c:64
void P_allocate(Complex **p, Complex **r, Complex **x1, Complex **x2, Complex *clover[2])
Allocates double precision memory needed for Congradp. Just to improve readability Note that since C ...
Definition congrad.c:140
void Q_free(Complex **p, Complex **x1, Complex **x2, Complex *clover[2])
Frees double precision memory needed for Congradq. Just to improve readability Note that since C does...
Definition congrad.c:211
void P_free(Complex **p, Complex **r, Complex **x1, Complex **x2, Complex *clover[2])
Frees memory needed for Congradp. Just to improve readability Note that since C does not modify it's ...
Definition congrad.c:263
void Q_free_f(Complex_f **p_f, Complex_f **x1_f, Complex_f **x2_f, Complex_f **r_f, Complex_f **X1_f)
Frees memory needed for Congradq. Just to improve readability Note that since C does not modify it's ...
Definition congrad.c:181
void P_allocate_f(Complex_f **p_f, Complex_f **r_f, Complex_f **x1_f, Complex_f **x2_f, Complex_f **xi_f)
Allocates memory needed for Congradp. Just to improve readability Note that since C does not modify i...
Definition congrad.c:103
int ComplexConvert(Complex_f *a, Complex *b, const unsigned int len, const bool dtof, const unsigned short stride)
takes an array of complex float and double precision numbers and converts the precision
Definition coord.c:420
__device__ __forceinline__ T conj(const T &z)
Complex Conjugation.
Definition cusu2hmc.cu:33
void cuMixed_Sumto(double *d, float *f, const unsigned int n, const dim3 dimGrid, const dim3 dimBlock)
Add a single to a double value, and save the output in the double array For complex valued arrays,...
void P_free_f(Complex_f **p_f, Complex_f **r_f, Complex_f **x1_f, Complex_f **x2_f, Complex_f **xi_f)
Frees memory needed for Congradp. Just to improve readability Note that since C does not modify it's ...
Definition congrad.c:243
int Congradp(int na, double res, Complex *Phi, Complex *xi, Complex *ud[2], Complex_f *ut[2], Complex_f *clover_f[nc], unsigned int *iu, unsigned int *id, Complex gamval[20], Complex_f gamval_f[20], const unsigned short gamin[16], Complex *sigval, Complex_f *sigval_f, unsigned short *sigin, double *dk[2], float *dk_f[2], Complex_f jqq, float akappa, float c_sw, int *itercg)
Matrix Inversion via Conjugate Gradient (no up/down flavour partitioning). Solves The matrix multipl...
Definition congrad.c:735
int Congradq(int na, double res, Complex *X1, Complex *r, Complex *ud[2], Complex_f *ut[2], Complex_f *clover_f[nc], unsigned int *iu, unsigned int *id, Complex gamval[20], Complex_f gamval_f[20], const unsigned short gamin[16], Complex *sigval, Complex_f *sigval_f, unsigned short *sigin, double *dk[2], float *dk_f[2], Complex_f jqq, float akappa, float c_sw, int *itercg)
Matrix Inversion via Conjugate Gradient (up/down flavour partitioning). Solves Implements up/down pa...
Definition congrad.c:278
int Par_fsum(float *dval)
Performs a reduction on a float dval to get a sum which is then distributed to all ranks.
int Par_dsum(double *dval)
Performs a reduction on a double dval to get a sum which is then distributed to all ranks.
Matrix multiplication and related declarations.
int rank
The MPI rank.
Definition par_mpi.c:20
#define AVX
Alignment of arrays. 64 for AVX-512, 32 for AVX/AVX2. 16 for SSE. Since AVX is standard on modern x86...
Definition sizes.h:279
#define nc
Colours.
Definition sizes.h:182
#define ngorkov
Gor'kov indices.
Definition sizes.h:190
#define kferm2Halo
Dirac lattice and halo.
Definition sizes.h:238
#define niterc
Hard limit for runaway trajectories in Conjugate gradient.
Definition sizes.h:172
#define kvol
Sublattice volume.
Definition sizes.h:163
#define Complex
Double precision complex number.
Definition sizes.h:64
#define kferm
sublattice size including Gor'kov indices
Definition sizes.h:195
#define ndirac
Dirac indices.
Definition sizes.h:186
#define cudaDeviceSynchronise()
Get rid of that bastardised yankee English.
Definition sizes.h:53
cublasHandle_t cublas_handle
Handle for cuBLAS.
Definition main.c:47
#define Complex_f
Single precision complex number.
Definition sizes.h:62
dim3 dimGrid
Default grid size. First component is normally nt. Second and third depend whatever is needed to get ...
Definition cusu2hmc.cu:27
#define kferm2
sublattice size including Dirac indices
Definition sizes.h:197
#define kvolHalo
Subvolume + halo size.
Definition sizes.h:234
#define kfermHalo
Gor'kov lattice and halo.
Definition sizes.h:236
dim3 dimBlock
Default block size. Usually 128.
Definition cusu2hmc.cu:25
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.