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/256.0f;
297 alignas(16) 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 //Copy of the source, so the true residual can be recomputed in double precision (reliable updates)
314 Complex *b0;
315#ifdef USE_GPU
316 cudaMallocAsync((void **)&b0,kferm2*sizeof(Complex),streams[5]);
317#else
318 b0=(Complex *)aligned_alloc(AVX,kferm2*sizeof(Complex));
319#endif
320
321 //Instead of copying element-wise in a loop, use memcpy.
322 //Get X1 in single precision
323 //Since X1 has a halo and X1_f does not we have to do this manually
324#if (nproc>1)
325 for(unsigned short j=0;j<nc*ndirac;j++)
326 ComplexConvert(X1_f+j*kvol,X1+j*kvolHalo,kvol,true,1);
327#else
328 ComplexConvert(X1_f,X1,kferm2,true,1);
329#endif
330 ComplexConvert(r_f,r,kferm2,true,1);
331 //And clover in double
332 if(c_sw){
333 ComplexConvert(clover_f[0],clover[0],6*kvol,false,1);
334 ComplexConvert(clover_f[1],clover[1],6*kvol,false,1);
335 }
336#ifdef USE_GPU
337 //Ensure conversion is done
339 //Needs to be strided
340#if (nproc>1)
341 for(unsigned int j=0;j<nc*ndirac;j++)
342 cudaMemcpyAsync(p_f+j*kvolHalo, X1_f+j*kvol, kvol*sizeof(Complex_f),cudaMemcpyDefault,streams[j]);
343#else
344 cudaMemcpyAsync(p_f, X1_f, kferm2*sizeof(Complex_f),cudaMemcpyDefault,streams[0]);
345#endif
346 cudaMemcpyAsync(b0,r,kferm2*sizeof(Complex),cudaMemcpyDefault,streams[1]);
348#else
349#pragma omp parallel for simd aligned(X1_f,r_f,X1,r:AVX)
350 for(unsigned int j=0;j<nc*ndirac;j++)
351 memcpy(p_f+j*kvolHalo, X1_f+j*kvol, kvol*sizeof(Complex_f));
352 memcpy(b0,r,kferm2*sizeof(Complex));
353#endif
354
355 alignas(16) double betan=1;double beta_max=FLT_MAX; bool do_dp=true;
356 for(*itercg=0; *itercg<niterc; (*itercg)++){
357 if(do_dp){
358#ifdef _DEBUGCG
359 if(!rank)
360 printf("Going to double precision on iteration %d. betan %e\talpha %e.\n",
361 *itercg,betan,creal(alpha));
362#elifdef _DEBUG
363 if(!rank)
364 printf("\nGoing to double precision on iteration %d. betan %e\talpha %e.\n",
365 *itercg,betan,creal(alpha));
366#endif
367 ComplexConvert(r_f,r,kferm2,false,1);
368 ComplexConvert(p_f,p,kvol,false,nc*ndirac);
369 //Update the residue vector, but not on the first call.
370 if(*itercg){
371#ifdef USE_GPU
372 //TODO: Check for multi-gpu. I fear this will get messy
373 cuMixed_Sumto((double *)X1,(float *)X1_f,2*kferm2,dimGrid,dimBlock);
374 //Bring everything into double precision
375 //Reset X1_f to zero.
376#else
377 //Update the residue vector, but not on the first call.
378#pragma omp parallel for simd collapse(2) aligned(X1,X1_f:AVX)
379 for(unsigned short j=0;j<nc*ndirac;j++)
380 for(unsigned int i=0;i<kvol;i++){
381 X1[i+j*kvolHalo]+=(Complex)X1_f[i+j*kvol];
382 }
383#endif
384 //Recompute residue for double precision update instead of promoting erronous single precision
385 Hdslash(x1,X1,ud,iu,id,gamval,gamin,dk,akappa);
386 if(c_sw)
387 HbyClover(x1,X1,clover,sigval,akappa,sigin,false);
388 Hdslashd(x2,x1,ud,iu,id,gamval,gamin,dk,akappa);
389 if(c_sw)
390 HbyClover(x2,x1,clover,sigval,akappa,sigin,true);
391#ifdef USE_GPU
393 cudaMemcpy(r,b0,kferm2*sizeof(Complex),cudaMemcpyDefault);
394 alignas(16) Complex m_one=-1.0;
395 cublasZaxpy(cublas_handle,kferm2,(cuDoubleComplex *)&m_one,(cuDoubleComplex *)x2,1,(cuDoubleComplex *)r,1);
396 if(fac_f!=0){
397 alignas(16) Complex m_fac=-(Complex)fac_f;
398#if(nproc>1)
399 for(unsigned short j=0;j<nc*ndirac;j++)
400 cublasZaxpy(cublas_handle,kvol,(cuDoubleComplex *)&m_fac,(cuDoubleComplex *)X1+j*kvolHalo,1,(cuDoubleComplex *)r+j*kvol,1);
401#else
402 cublasZaxpy(cublas_handle,kferm2,(cuDoubleComplex *)&m_fac,(cuDoubleComplex *)X1,1,(cuDoubleComplex *)r,1);
403#endif
404 }
405#else
406 const double fac_d=crealf(fac_f);
407#pragma omp parallel for simd collapse(2)
408 for(unsigned short j=0;j<nc*ndirac;j++)
409 for(unsigned int i=0;i<kvol;i++)
410 r[i+j*kvol]=b0[i+j*kvol]-x2[i+j*kvol]-fac_d*X1[i+j*kvolHalo];
411#endif
412 }
414 //No need to synchronise here. The memcpy in Hdslash is blocking
415 Hdslash(x1,p,ud,iu,id,gamval,gamin,dk,akappa);
416 //Clover contribution
417 if(c_sw)
418 HbyClover(x1,p,clover,sigval,akappa,sigin,false);
419 Hdslashd(x2,x1,ud,iu,id,gamval,gamin,dk,akappa);
420 //Clover contribution
421 if(c_sw)
422 HbyClover(x2,x1,clover,sigval,akappa,sigin,true);
423#ifdef USE_GPU
425#endif
426 if(fac_f!=0){
427 alignas(16) const Complex fac=(Complex)fac_f;
428#ifdef USE_GPU
429 //Multiple ranks means we need striding
430#if (nproc>1)
431 for(unsigned short j=0;j<nc*ndirac;j++)
432 cublasZaxpy(cublas_handle,kvol,(cuDoubleComplex *)&fac,(cuDoubleComplex *)p+j*kvolHalo,1,(cuDoubleComplex *)x2+j*kvol,1);
433#else
434 //Single GPU, no halos, so make it one call to speed things up
435 cublasZaxpy(cublas_handle,kferm2,(cuDoubleComplex *)&fac,(cuDoubleComplex *)p,1,(cuDoubleComplex *)x2,1);
436#endif
437#elif defined USE_BLAS
438 for(unsigned short j=0;j<nc*ndirac;j++)
439 cblas_zaxpy(kvol, &fac, p+j*kvolHalo, 1, x2+j*kvol, 1);
440#else
441#pragma omp parallel for simd collapse(2) aligned(p,x2:AVX)
442 for(unsigned short j=0;j<nc*ndirac;j++)
443 for(unsigned int i=0; i<kvol; i++)
444 x2[i+j*kvol]+=fac*p[i+j*kvolHalo];
445#endif
446 }
447
449 if(*itercg){
450 alpha=0;
451#ifdef USE_GPU
452#if (nproc>1)
453 for(unsigned short j=0;j<nc*ndirac;j++){
454 Complex alpha_t=0;
455 cublasZdotc(cublas_handle,kvol,(cuDoubleComplex *)p+j*kvolHalo,1,(cuDoubleComplex *)x2+j*kvol,1,(cuDoubleComplex *)&alpha_t);
456 alpha+=alpha_t;
457 }
458#else
459 cublasZdotc(cublas_handle,kferm2,(cuDoubleComplex *)p,1,(cuDoubleComplex *)x2,1,(cuDoubleComplex *)&alpha);
460#endif
461#elif defined USE_BLAS
462 for(unsigned short j=0;j<nc*ndirac;j++){
463 Complex alpha_t=0;
464 cblas_zdotc_sub(kvol, p+j*kvolHalo, 1, x2+j*kvol, 1, &alpha_t);
465 alpha+=creal(alpha_t);
466 }
467#else
468#pragma omp parallel for simd collapse(2) aligned(p,x2:AVX) reduction(+:alpha)
469 for(unsigned short j=0;j<nc*ndirac;j++)
470 for(unsigned int i=0; i<kvol; i++)
471 alpha+=conj(p[i+j*kvolHalo])*x2[i+j*kvol];
472#endif
473 //For now I'll cast it into a float for the reduction. Each rank only sends and writes
474 //to the real part so this is fine
475#if (nproc>1)
476 Par_dsum((double *)&alpha);
477#endif
479 alpha=alphan/creal(alpha);
481#ifdef USE_GPU
482#if (nproc>1)
483 for(unsigned short j=0;j<nc*ndirac;j++)
484 cublasZaxpy(cublas_handle,kvol,(cuDoubleComplex *)&alpha,(cuDoubleComplex *)p+j*kvolHalo,1,(cuDoubleComplex *)X1+j*kvolHalo,1);
485#else
486 cublasZaxpy(cublas_handle,kferm2,(cuDoubleComplex *)&alpha,(cuDoubleComplex *)p,1,(cuDoubleComplex *)X1,1);
487#endif
488#elif defined USE_BLAS
489 for(unsigned short j=0;j<nc*ndirac;j++)
490 cblas_zaxpy(kvol, &alpha, p+j*kvolHalo, 1, X1+j*kvolHalo, 1);
491#else
492#pragma omp parallel for simd collapse(2) aligned(p,X1:AVX)
493 for(unsigned short j=0;j<nc*ndirac;j++)
494 for(unsigned int i=0; i<kvol; i++)
495 X1[i+j*kvolHalo]+=alpha*p[i+j*kvolHalo];
496#endif
497 }
498
499#ifdef USE_GPU
500 alignas(16) Complex alpha_m=(Complex)(-alpha);
501 cublasZaxpy(cublas_handle, kferm2,(cuDoubleComplex *)&alpha_m,(cuDoubleComplex *)x2,1,(cuDoubleComplex *)r,1);
502 alignas(16) double betan_d;
503 cublasDznrm2(cublas_handle,kferm2,(cuDoubleComplex *)r,1,&betan_d);
504 betan=betan_d*betan_d;
505#elif defined USE_BLAS
506 const Complex alpha_m = (Complex)(-alpha);
507 cblas_zaxpy(kferm2, &alpha_m, x2, 1, r, 1);
508 //Undo the negation for the BLAS routine
509 double betan_d = cblas_dznrm2(kferm2, r,1);
510 //Gotta square it to "undo" the norm
511 betan = betan_d*betan_d;
512#else
513 betan=0;
514#pragma omp parallel for simd aligned(r_f,x2_f:AVX) reduction(+:betan)
515 for(unsigned int i=0; i<kferm2; i++){
516 r[i]-=alpha*x2[i];
517 betan += conj(r[i])*r[i];
518 }
519 alignas(16) double betan_d=sqrt(betan);
520#endif
521 //And... reduce.
522#if (nproc>1)
523 Par_dsum(&betan);
524#endif
525 betan_d=sqrt(betan);
526 //Update beta_max. Mandatory for double precision.
527 beta_max=betan_d;
528#ifdef _DEBUG
529 if(!rank) printf("DP Iter(CG)=%i\tbeta_n=%e\talpha=%e\n", *itercg, betan, creal(alpha));
530 fflush(stdout);
531#endif
532 alignas(16) const Complex beta = (*itercg) ? betan/betad : 0;
533 betad=betan; alphan=betan;
534#ifdef USE_GPU
535 cudaMemsetAsync(X1_f,0,kferm2*sizeof(Complex_f),streams[0]);
536 alpha_m=1;
537 //Strided multi-gpu
538#if (nproc>1)
539 for(unsigned short j=0;j<nc*ndirac;j++){
540 cublasZdscal(cublas_handle,kvol,(double *)&beta,(cuDoubleComplex *)p+j*kvolHalo,1);
541 cublasZaxpy(cublas_handle,kvol,(cuDoubleComplex *)&alpha_m,(cuDoubleComplex *)r+j*kvol,1,(cuDoubleComplex *)p+j*kvolHalo,1);
542 }
543 //And single GPU
544#else
545 cublasZdscal(cublas_handle,kferm2,(double *)&beta,(cuDoubleComplex *)p,1);
546 cublasZaxpy(cublas_handle,kferm2,(cuDoubleComplex *)&alpha_m,(cuDoubleComplex *)r,1,(cuDoubleComplex *)p,1);
547#endif
548#elif (defined __USE_MKL__||defined OPENBLAS||defined AMD_BLAS)
549 memset(X1_f,0,kferm2*sizeof(Complex_f));
550 const Complex a = 1.0;
551 //There is cblas_?axpby in the MKL and AMD though, set a = 1 and b = \beta.
552 //If we get a small enough \beta_n before hitting the iteration cap we break
553 for(unsigned short j=0;j<nc*ndirac;j++)
554 cblas_zaxpby(kvol, &a, r+j*kvol, 1, &beta, p+j*kvolHalo, 1);
555#elifdef USE_BLAS
556 memset(X1_f,0,kferm2*sizeof(Complex_f));
557 const Complex a = 1.0;
558 for(unsigned short j=0;j<nc*ndirac;j++){
559 cblas_zscal(kvol,&beta,p+j*kvolHalo,1);
560 cblas_zaxpy(kvol,&a,r+j*kvol,1,p+j*kvolHalo,1);
561 }
562#else
563 memset(X1_f,0,kferm2*sizeof(Complex_f));
564#pragma omp parallel for simd collapse(2) aligned(r,p:AVX)
565 for(unsigned short j=0;j<nc*ndirac;j++)
566 for(unsigned int i=0; i<kvol; i++)
567 p[i+j*kvolHalo]=r[i+j*kvol]+beta*p[i+j*kvolHalo];
568#endif
569 ComplexConvert(p_f,p,kvol,true,nc*ndirac);
570 ComplexConvert(r_f,r,kferm2,true,1);
571#ifdef _DEBUGCG
572 if(!rank) printf("Double precision. Iter(CG)=%i\tbeta_n=%e\talpha=%e\n", *itercg, betan, creal(alpha));
573 fflush(stdout);
574#endif
575 if(betan<resid){
576 (*itercg)++;
577#ifdef _DEBUG
578 if(!rank) printf("\nIter(CG)=%i\tResidue: %e\tTolerance: %e\n", *itercg, betan, resid);
579#endif
580 ret_val=0; break;
581 }
582 else
583 do_dp=false;
584 }
585 else{
587 //No need to synchronise here. The memcpy in Hdslash is blocking
588 Hdslash_f(x1_f,p_f,ut,iu,id,gamval_f,gamin,dk_f,akappa);
589 //Clover contribution
590 if(c_sw)
591 HbyClover_f(x1_f,p_f,clover_f,sigval_f,akappa,sigin,false);
592 Hdslashd_f(x2_f,x1_f,ut,iu,id,gamval_f,gamin,dk_f,akappa);
593 //Clover contribution
594 if(c_sw)
595 HbyClover_f(x2_f,x1_f,clover_f,sigval_f,akappa,sigin,true);
596#ifdef USE_GPU
598#endif
600 //No point adding zero a couple of hundred times if the diquark source is zero
601 if(fac_f!=0){
602#ifdef USE_GPU
603 //Strided multi-gpu
604#if (nproc>1)
605 for(unsigned short j=0;j<nc*ndirac;j++)
606 cublasCaxpy(cublas_handle,kvol,(cuComplex *)&fac_f,(cuComplex *)p_f+j*kvolHalo,1,(cuComplex *)x2_f+j*kvol,1);
607 //Single GPU
608#else
609 cublasCaxpy(cublas_handle,kferm2,(cuComplex *)&fac_f,(cuComplex *)p_f,1,(cuComplex *)x2_f,1);
610#endif
611#elif defined USE_BLAS
612 for(unsigned short j=0;j<nc*ndirac;j++)
613 cblas_caxpy(kvol, &fac_f, p_f+j*kvolHalo, 1, x2_f+j*kvol, 1);
614#else
615#pragma omp parallel for simd collapse(2) aligned(p_f,x2_f:AVX)
616 for(unsigned short j=0;j<nc*ndirac;j++)
617 for(unsigned int i=0; i<kvol; i++)
618 x2_f[i+j*kvol]+=fac_f*p_f[i+j*kvolHalo];
619#endif
620 }
623 if(*itercg){
624 alphad=0;
625#ifdef USE_GPU
626#if (nproc>1)
627 for(unsigned short j=0;j<nc*ndirac;j++){
628 Complex alpha_t=0;
629 cublasCdotc(cublas_handle,kvol,(cuComplex *)p_f+j*kvolHalo,1,(cuComplex *)x2_f+j*kvol,1,(cuComplex *)&alpha_t);
630 alphad+=alpha_t;
631 }
632#else
633 cublasCdotc(cublas_handle,kferm2,(cuComplex *)p_f,1,(cuComplex *)x2_f,1,(cuComplex *)&alphad);
634#endif
635#elif defined USE_BLAS
636 for(unsigned short j=0;j<nc*ndirac;j++){
637 Complex_f alpha_t=0;
638 cblas_cdotc_sub(kvol, p_f+j*kvolHalo, 1, x2_f+j*kvol, 1, &alpha_t);
639 alphad+=creal(alpha_t);
640 }
641#else
642#pragma omp parallel for simd aligned(p_f,x2_f:AVX) reduction(alphad:+)
643 for(unsigned short j=0;j<nc*ndirac;j++)
644 for(unsigned int i=0; i<kvol; i++)
645 alphad+=conj(p_f[i+j*kvolHalo])*x2_f[i+j*kvol];
646#endif
647 //For now I'll cast it into a float for the reduction. Each rank only sends and writes
648 //to the real part so this is fine
649#if (nproc>1)
650 Par_fsum((float *)&alphad);
651#endif
653 alpha=alphan/creal(alphad);
655#ifdef USE_GPU
656 alignas(8) Complex_f alpha_f = (Complex_f)alpha;
657#if (nproc>1)
658 for(unsigned short j=0;j<nc*ndirac;j++)
659 cublasCaxpy(cublas_handle,kvol,(cuComplex *)&alpha_f,(cuComplex *)p_f+j*kvolHalo,1,(cuComplex *)X1_f+j*kvol,1);
660#else
661 cublasCaxpy(cublas_handle,kferm2,(cuComplex *)&alpha_f,(cuComplex *)p_f,1,(cuComplex *)X1_f,1);
662#endif
663#elif defined USE_BLAS
664 Complex_f alpha_f = (Complex_f)alpha;
665 for(unsigned short j=0;j<nc*ndirac;j++)
666 cblas_caxpy(kvol, &alpha_f, p_f+j*kvolHalo, 1, X1_f+j*kvol, 1);
667#else
668#pragma omp parallel for simd collapse(2) aligned(X1_f,p_f:AVX)
669 for(unsigned short j=0;j<nc*ndirac;j++)
670 for(unsigned int i=0; i<kvol; i++)
671 X1_f[i+j*kvol]+=alpha*p_f[i+j*kvolHalo];
672#endif
673 }
675 // And no Halos here so nice and easy
676#ifdef USE_GPU
677 alignas(8) __managed__ Complex_f alpha_m=(Complex_f)(-alpha);
678 cublasCaxpy(cublas_handle, kferm2,(cuComplex *)&alpha_m,(cuComplex *)x2_f,1,(cuComplex *)r_f,1);
679 alignas(8) float betan_f;
680 cublasScnrm2(cublas_handle,kferm2,(cuComplex *)r_f,1,&betan_f);
681 betan = betan_f*betan_f;
682#elif defined USE_BLAS
683 const Complex_f alpha_m = (Complex_f)(-alpha);
684 cblas_caxpy(kferm2, &alpha_m, x2_f, 1, r_f, 1);
685 //Undo the negation for the BLAS routine
686 float betan_f = cblas_scnrm2(kferm2, r_f,1);
687 //Gotta square it to "undo" the norm
688 betan = betan_f*betan_f;
689#else
690 betan=0;
691#pragma omp parallel for simd aligned(r_f,x2_f:AVX) reduction(+:betan)
692 for(unsigned int i=0; i<kferm2; i++){
693 r_f[i]-=alpha*x2_f[i];
694 betan += conj(r_f[i])*r_f[i];
695 }
696 alignas(8) float betan_f=sqrt(betan);
697#endif
698 //And... reduce.
699#if (nproc>1)
700 Par_dsum(&betan);
701#endif
702 betan_f=sqrt(betan);
703 //Update beta_max if needed. On paper congrad is monotonically decreasing
704 beta_max = (betan_f>beta_max) ?betan_f : beta_max;
705
706#ifdef _DEBUG
707 if(!rank) printf("Iter(CG)=%i\tbeta_n=%e\talpha=%e%s", *itercg, betan, creal(alpha),endline);
708 fflush(stdout);
709#endif
710 if(betan_f<beta_max*d_prec){
711#ifdef _DEBUG
712 if(!rank)
713 printf("\nResidue %e is less than %e times %e.%s",betan_f,d_prec,beta_max,endline);
714#endif
715 do_dp=true;
716 }
717 else if(betan<resid){
718#ifdef _DEBUG
719 if(!rank)
720 printf("\nBetan %e is less than target residue %e.\n",betan,resid);
721#endif
722 do_dp=true;
723 //break;
724 }
725 else if(*itercg==niterc-1){
726 if(!rank) fprintf(stderr, "\nWarning %i in %s: Exceeded iteration limit %i beta_n=%e\n",\
727 ITERLIM, funcname, *itercg, betan);
728 ret_val=ITERLIM; break;
729 }
730 //Here we evaluate beta=(r_{k+1}.r_{k+1})/(r_k.r_k) and then shuffle our indices down the line.
731 //On the first iteration we define beta to be zero.
732 //Note that beta below is not the global beta and scoping is used to avoid conflict between them
733 Complex beta = (*itercg) ? betan/betad : 0;
734 betad=betan; alphan=betan;
735 //BLAS for p=r+\betap doesn't exist in standard BLAS. This is NOT an axpy case as we're multiplying y by
736 //\beta instead of x.
737#ifdef USE_GPU
738 alignas(8) Complex_f beta_f=(Complex_f)beta;
739 alpha_m = 1.0;
740#if (nproc>1)
741 for(unsigned short j=0;j<nc*ndirac;j++){
742 cublasCscal(cublas_handle,kvol,(cuComplex *)&beta_f,(cuComplex *)p_f+j*kvolHalo,1);
743 cublasCaxpy(cublas_handle,kvol,(cuComplex *)&alpha_m,(cuComplex *)r_f+j*kvolHalo,1,(cuComplex *)p_f+j*kvolHalo,1);
744 }
745#else
746 cublasCscal(cublas_handle,kferm2,(cuComplex *)&beta_f,(cuComplex *)p_f,1);
747 cublasCaxpy(cublas_handle,kferm2,(cuComplex *)&alpha_m,(cuComplex *)r_f,1,(cuComplex *)p_f,1);
748#endif
749#elif (defined __USE_MKL__||defined OPENBLAS||defined AMD_BLAS)
750 Complex_f a = 1.0;
751 Complex_f beta_f=(Complex_f)beta;
752 //There is cblas_?axpby in the MKL and AMD though, set a = 1 and b = \beta.
753 //If we get a small enough \beta_n before hitting the iteration cap we break
754 for(unsigned short j=0;j<nc*ndirac;j++)
755 cblas_caxpby(kvol, &a, r_f+j*kvol, 1, &beta_f, p_f+j*kvolHalo, 1);
756#elif defined USE_BLAS
757 Complex_f beta_f=(Complex_f)beta;
758 Complex_f a = 1.0;
759 for(unsigned short j=0;j<nc*ndirac;j++){
760 cblas_cscal(kvol,&beta_f,p_f+j*kvolHalo,1);
761 cblas_caxpy(kvol,&a,r_f+j*kvol,1,p_f+j*kvolHalo,1);
762 }
763#else
764#pragma omp parallel for simd collapse(2) aligned(p_f,r_f:AVX)
765 for(unsigned short j=0;j<nc*ndirac;j++)
766 for(unsigned int i=0; i<kvol; i++)
767 p_f[i+j*kvolHalo]=r_f[i+j*kvol]+beta*p_f[i+j*kvolHalo];
768#endif
769 }
770 }
771 Q_free_f(&p_f,&x1_f,&x2_f,&r_f,&X1_f);
772 Q_free(&p,&x1,&x2,clover);
773#ifdef USE_GPU
774 cudaFreeAsync(b0,streams[1]);
775#else
776 free(b0);
777#endif
778 return ret_val;
779}
780int Congradp(int na, double res, Complex *Phi, Complex *xi, Complex *ud[2], Complex_f *ut[2], Complex_f *clover_f[nc],
781 unsigned int *iu, unsigned int *id, Complex gamval[20], Complex_f gamval_f[20], const unsigned short gamin[16],
782 Complex *sigval, Complex_f *sigval_f,unsigned short *sigin, double *dk[2], float *dk_f[2],
783 Complex_f jqq,float akappa,float c_sw,int *itercg){
784 const char funcname[] = "Congradp";
785 //Return value
786 int ret_val=0;
787 const double resid = res*res;
788#ifdef _DEBUGCG
789#warning "CG Debugging"
790 char *endline = "\n";
791#else
792 char *endline = "\r";
793#endif
794
797 const float d_prec=1.0f/256.0f;
798
799 //These were evaluated only in the first loop of niterx so we'll just do it outside of the loop.
800 alignas(8) double alphan=1.0;
801 //These alpha and beta terms should be double, but that causes issues with BLAS. Instead we declare
802 //them Complex and work with the real part (especially for \alpha_d)
803 //Give initial values Will be overwritten if niterx>0
804 alignas(8) double betad = 1.0; alignas(8) Complex_f alphad=0; alignas(16)Complex alpha = 1; //Alignment needed for cuBLAS
805
806 Complex_f *p_f, *r_f, *x1_f, *x2_f, *xi_f;
807 Complex *p, *r, *x1, *x2, *clover[nc];
808 P_allocate_f(&p_f,&r_f,&x1_f,&x2_f,&xi_f);
809 P_allocate(&p, &r, &x1, &x2,clover);
810
811 ComplexConvert(r_f,Phi+na*kferm,kferm,true,1);
812 ComplexConvert(xi_f,xi,kferm,true,1);
813 if(c_sw){
814 ComplexConvert(clover_f[0],clover[0],6*kvol,false,1);
815 ComplexConvert(clover_f[1],clover[1],6*kvol,false,1);
816 }
817 //Instead of copying element-wise in a loop, use memcpy.
818#ifdef USE_GPU
819 //Get r in single precision
820 //Get xi in single precision
821 cudaMemcpy(r,Phi+na*kferm,kferm*sizeof(Complex),cudaMemcpyDefault);
822#if (nproc>1)//strided memcpy
823 for(unsigned short j=0;j<nc*ngorkov;j++){
824 cudaMemcpyAsync(p+j*kvolHalo,xi+j*kvol,kvol*sizeof(Complex),cudaMemcpyDefault,streams[j]);
825 cudaMemcpyAsync(p_f+j*kvolHalo,xi_f+j*kvol,kvol*sizeof(Complex_f),cudaMemcpyDefault,streams[j]);
826 }
827#else
828 cudaMemcpyAsync(p,xi,kferm*sizeof(Complex),cudaMemcpyDefault,streams[0]);
829 cudaMemcpyAsync(p_f,xi_f,kferm*sizeof(Complex_f),cudaMemcpyDefault,streams[1]);
830#endif
832#else
833 memcpy(r,Phi+na*kferm,kferm*sizeof(Complex));
834 //These strided loops for MPI halos are a total pain in the arse...
835 for(unsigned short j=0;j<nc*ngorkov;j++){
836 memcpy(p+j*kvolHalo,xi+j*kvol,kvol*sizeof(Complex));
837 memcpy(p_f+j*kvolHalo,xi_f+j*kvol,kvol*sizeof(Complex_f));
838 }
839#endif
840
841 double betan=1;double beta_max=FLT_MAX; bool do_dp=true;
842 for((*itercg)=0; (*itercg)<niterc; (*itercg)++){
843 //Don't overwrite on first run.
844 if(do_dp){
845#ifdef _DEBUGCG
846 if(!rank)
847 printf("Going to double precision on iteration %d. %sbetan %e\talpha %e. %s",
848 *itercg,endline,betan,creal(alpha),endline);
849#elifdef _DEBUG
850 if(!rank)
851 printf("\nGoing to double precision on iteration %d. %sbetan %e\talpha %e.\n",
852 *itercg,endline,betan,creal(alpha));
853#endif
854 ComplexConvert(r_f,r,kferm,false,1);
855 //TODO: Banking on converting the halo too being faster than multiple launches
856 ComplexConvert(p_f,p,kvol,false,nc*ngorkov);
857 //Update the residue vector, but not on the first call.
858 if(*itercg){
859#ifdef USE_GPU
860 cuMixed_Sumto((double *)xi,(float *)xi_f,2*kferm,dimGrid,dimBlock);
861 //Bring everything into double precision
862 //Reset xi_f to zero.
864#else
865#pragma omp parallel for simd aligned(xi,xi_f:AVX)
866 for(unsigned int i=0;i<kferm;i++)
867 xi[i]+=(Complex)xi_f[i];
868#endif
869#if(nproc>1)
870 //Dslash needs its input laid out with halos, xi has none
871 Complex *xh;
872#ifdef USE_GPU
873 cudaMallocAsync((void **)&xh,kfermHalo*sizeof(Complex),streams[0]);
874 for(unsigned short j=0;j<nc*ngorkov;j++)
875 cudaMemcpyAsync(xh+j*kvolHalo,xi+j*kvol,kvol*sizeof(Complex),cudaMemcpyDefault,streams[0]);
877#else
878 xh=(Complex *)aligned_alloc(AVX,kfermHalo*sizeof(Complex));
879 for(unsigned short j=0;j<nc*ngorkov;j++)
880 memcpy(xh+j*kvolHalo,xi+j*kvol,kvol*sizeof(Complex));
881#endif
882#else
883 Complex *xh=xi;
884#endif
885 Dslash(x1,xh,ud,iu,id,gamval,gamin,dk,jqq,akappa);
886 if(c_sw)
887 ByClover(x1,xh,clover,sigval,akappa,sigin,false);
888 Dslashd(x2,x1,ud,iu,id,gamval,gamin,dk,jqq,akappa);
889 if(c_sw)
890 ByClover(x2,x1,clover,sigval,akappa,sigin,true);
891#ifdef USE_GPU
893#if(nproc>1)
894 cudaFreeAsync(xh,streams[0]);
895#endif
896 cudaMemcpy(r,Phi+na*kferm,kferm*sizeof(Complex),cudaMemcpyDefault);
897 alignas(16) Complex m_one=-1.0;
898 cublasZaxpy(cublas_handle,kferm,(cuDoubleComplex *)&m_one,(cuDoubleComplex *)x2,1,(cuDoubleComplex *)r,1);
899#else
900#if(nproc>1)
901 free(xh);
902#endif
903#pragma omp parallel for simd
904 for(unsigned int i=0;i<kferm;i++)
905 r[i]=Phi[i+na*kferm]-x2[i];
906#endif
907 }
909 //No need to synchronise here. The memcpy in Dslash is blocking
910 Dslash(x1,p,ud,iu,id,gamval,gamin,dk,jqq,akappa);
911 if(c_sw)
912 ByClover(x1,p,clover,sigval,akappa,sigin,false);
913 Dslashd(x2,x1,ud,iu,id,gamval,gamin,dk,jqq,akappa);
914 if(c_sw)
915 ByClover(x2,x1,clover,sigval,akappa,sigin,true);
916#ifdef USE_GPU
918#endif
919
921 if(*itercg){
922 alpha=0;
923#ifdef USE_GPU
924#if (nproc>1)//Strided
925 for(unsigned short j=0;j<nc*ngorkov;j++){
926 alignas(16) Complex alpha_t=0;
927 cublasZdotc(cublas_handle,kvol,(cuDoubleComplex *)p+j*kvolHalo,1,(cuDoubleComplex *)x2+j*kvol,1,(cuDoubleComplex *)&alpha_t);
928 alpha+=alpha_t
929 }
930#else
931 cublasZdotc(cublas_handle,kferm,(cuDoubleComplex *)p,1,(cuDoubleComplex *)x2,1,(cuDoubleComplex *)&alpha);
932#endif
933#elif defined USE_BLAS
934 for(unsigned short j=0;j<nc*ngorkov;j++){
935 Complex alpha_t=0;
936 cblas_zdotc_sub(kvol, p+j*kvolHalo, 1, x2+j*kvol, 1, &alpha_t);
937 alpha+=alpha_t;
938 }
939#else
940#pragma omp parallel for simd collapse(2) aligned(p,x2:AVX) reduction(+:alpha)
941 for(unsigned short j=0;j<nc*ngorkov;j++)
942 for(unsigned int i=0; i<kvol; i++)
943 alpha+=conj(p[i+j*kvolHalo])*x2[i+j*kvol];
944#endif
945 //For now I'll cast it into a float for the reduction. Each rank only sends and writes
946 //to the real part so this is fine
947#if (nproc>1)
948 Par_dsum((double *)&alpha);
949#endif
951 alpha=alphan/creal(alpha);
953#ifdef USE_GPU
954#if (nproc>1)
955 for(unsigned short j=0;j<nc*ngorkov;j++)
956 cublasZaxpy(cublas_handle,kvol,(cuDoubleComplex *)&alpha,(cuDoubleComplex *)p+j*kvolHalo,1,(cuDoubleComplex *)xi+j*kvol,1);
957#else
958 cublasZaxpy(cublas_handle,kferm,(cuDoubleComplex *)&alpha,(cuDoubleComplex *)p,1,(cuDoubleComplex *)xi,1);
959#endif
960#elif defined USE_BLAS
961 for(unsigned short j=0;j<nc*ngorkov;j++)
962 cblas_zaxpy(kvol, &alpha, p+j*kvolHalo, 1, xi+j*kvol, 1);
963#else
964#pragma omp parallel for simd collapse(2) aligned(p,xi:AVX)
965 for(unsigned short j=0;j<nc*ngorkov;j++)
966 for(unsigned int i=0; i<kvol; i++)
967 xi[i+j*kvol]+=alpha*p[i+j*kvolHalo];
968#endif
969 }
970
971#ifdef USE_GPU
972 Complex alpha_m=(Complex)(-alpha);
973 cublasZaxpy(cublas_handle, kferm,(cuDoubleComplex *)&alpha_m,(cuDoubleComplex *)x2,1,(cuDoubleComplex *)r,1);
974 double betan_d;
975 cublasDznrm2(cublas_handle,kferm,(cuDoubleComplex *)r,1,&betan_d);
976 betan=betan_d*betan_d;
977#elif defined USE_BLAS
978 const Complex alpha_m = (Complex)(-alpha);
979 cblas_zaxpy(kferm, &alpha_m, x2, 1, r, 1);
980 //Undo the negation for the BLAS routine
981 double betan_d = cblas_dznrm2(kferm, r,1);
982 //Gotta square it to "undo" the norm
983 betan = betan_d*betan_d;
984#else
985 betan=0;
986#pragma omp parallel for simd aligned(r,x2:AVX) reduction(+:betan)
987 for(unsigned int i=0; i<kferm; i++){
988 r[i]-=alpha*x2[i];
989 betan += conj(r[i])*r[i];
990 }
991 double betan_d=sqrt(betan);
992#endif
993 //And... reduce.
994#if (nproc>1)
995 Par_dsum(&betan);
996#endif
997 betan_d=sqrt(betan);
998 //Update beta_max. Mandatory for double precision.
999 beta_max=betan_d;
1000#ifdef _DEBUG
1001 if(! rank) printf("DP Iter(CG)=%i\tbeta_n=%e\talpha=%e\n", *itercg, betan, creal(alpha));
1002 fflush(stdout);
1003#endif
1004 alignas(16) const Complex beta = (*itercg) ? betan/betad : 0;
1005 betad=betan; alphan=betan;
1006#ifdef USE_GPU
1007 cudaMemsetAsync(xi_f,0,kferm*sizeof(Complex_f),streams[0]);
1008 alpha_m=1;
1009#if (nproc>1)
1010 for(unsigned short j=0;j<nc*ngorkov;j++){
1011 cublasZdscal(cublas_handle,kvol,(double *)&beta,(cuDoubleComplex *)p+j*kvolHalo,1);
1012 cublasZaxpy(cublas_handle,kvol,(cuDoubleComplex *)&alpha_m,(cuDoubleComplex *)r+j*kvol,1,(cuDoubleComplex *)p+j*kvolHalo,1);
1013 }
1014#else
1015 cublasZdscal(cublas_handle,kferm,(double *)&beta,(cuDoubleComplex *)p,1);
1016 cublasZaxpy(cublas_handle,kferm,(cuDoubleComplex *)&alpha_m,(cuDoubleComplex *)r,1,(cuDoubleComplex *)p,1);
1017#endif
1019#elif (defined __USE_MKL__||defined OPENBLAS||defined AMD_BLAS)
1020 memset(xi_f,0,kferm*sizeof(Complex_f));
1021 const Complex a = 1.0;
1022 //There is cblas_? axpby in the MKL and AMD though, set a = 1 and b = \beta.
1023 //If we get a small enough \beta_n before hitting the iteration cap we break
1024 for(unsigned short j=0;j<nc*ngorkov;j++)
1025 cblas_zaxpby(kvol, &a, r+j*kvol, 1, &beta, p+j*kvolHalo, 1);
1026#elif defined USE_BLAS
1027 memset(xi_f,0,kferm*sizeof(Complex_f));
1028 const Complex a = 1.0;
1029 for(unsigned short j=0;j<nc*ngorkov;j++){
1030 cblas_zscal(kvol,&beta,p+j*kvolHalo,1);
1031 cblas_zaxpy(kvol,&a,r+j*kvol,1,p+j*kvolHalo,1);
1032 }
1033#else
1034 memset(xi_f,0,kferm*sizeof(Complex_f));
1035#pragma omp parallel for simd collapse(2) aligned(r,p:AVX)
1036 for(unsigned short j=0;j<nc*ngorkov;j++)
1037 for(unsigned int i=0; i<kvol; i++)
1038 p[i+j*kvolHalo]=r[i+j*kvol]+beta*p[i+j*kvolHalo];
1039#endif
1040 ComplexConvert(p_f,p,kvol,true,nc*ngorkov);
1041 ComplexConvert(r_f,r,kferm,true,1);
1042#ifdef _DEBUGCG
1043 if(! rank) printf("Double precision. Iter(CG)=%i\tbeta_n=%e\talpha=%e\n", *itercg, betan, creal(alpha));
1044 fflush(stdout);
1045#endif
1046 if(betan<resid){
1047 (*itercg)++;
1048#ifdef _DEBUG
1049 if(!rank) printf("\nIter(CG)=%i\tResidue: %e\tTolerance: %e\n", *itercg, betan, resid);
1050#endif
1051 ret_val=0; break;
1052 }
1053 else
1054 do_dp=false;
1055 }
1056 else{
1057 //x2=(M^\dagger)x1=(M^\dagger)Mp
1058 Dslash_f(x1_f,p_f,ut,iu,id,gamval_f,gamin,dk_f,jqq,akappa);
1059 if(c_sw)
1060 ByClover_f(x1_f,p_f,clover_f,sigval_f,akappa,sigin,false);
1061 Dslashd_f(x2_f,x1_f,ut,iu,id,gamval_f,gamin,dk_f,jqq,akappa);
1062 if(c_sw)
1063 ByClover_f(x2_f,x1_f,clover_f,sigval_f,akappa,sigin,true);
1064#ifdef USE_GPU
1066#endif
1067 //We can't evaluate \alpha on the first niterx because we need to get \beta_n.
1068 if(*itercg){
1069 //x*.x
1070 alphad=0;
1071#ifdef USE_GPU
1072#if(nproc>1)//strided
1073 for(unsigned short j=0;j<nc*ngorkov;j++){
1074 alignas(8) Complex_f alpha_t=0;
1075 cublasCdotc(cublas_handle,kvol,(cuComplex *)p_f+j*kvolHalo,1,(cuComplex *)x2_f+j*kvol,1,(cuComplex *)&alpha_t);
1076 alphad+=alpha_t;
1077 }
1078#else
1079 cublasCdotc(cublas_handle,kferm,(cuComplex *)p_f,1,(cuComplex *)x2_f,1,(cuComplex *)&alphad);
1080#endif
1081#elifdef USE_BLAS
1082 for(unsigned short j=0;j<nc*ngorkov;j++){
1083 alignas(8) Complex_f alpha_t=0;
1084 cblas_cdotc_sub(kvol,p_f+j*kvolHalo,1,x2_f+j*kvol,1,&alpha_t);
1085 alphad+=alpha_t;
1086 }
1087#else
1088#pragma omp parallel for simd collapse(2) aligned(p_f,x2_f:AVX) reduction(+:alphad)
1089 for(unsigned short j=0;j<nc*ngorkov;j++)
1090 for(unsigned int i = 0; i<kvol; i++)
1091 alphad+=conj(p_f[i+j*kvolHalo])*x2_f[i+j*kvol];
1092#endif
1093#if (nproc>1)
1094 Par_fsum((float *)&alphad);
1095#endif
1096 //alpha=(r.r)/p(M^\dagger)Mp
1097 alpha=alphan/creal(alphad);
1098 // Complex_f alpha_f = (Complex_f)alpha;
1099 //x+\alpha p
1100#ifdef USE_BLAS
1101 alignas(8) Complex_f alpha_f=(float)alpha;
1102#ifdef USE_GPU
1103#if (nproc>1) //strided
1104 for(unsigned short j=0;j<nc*ngorkov;j++)
1105 cublasCaxpy(cublas_handle,kvol,(cuComplex*) &alpha_f,(cuComplex*) p_f+j*kvolHalo,1,(cuComplex*) xi_f+j*kvol,1);
1106#else
1107 cublasCaxpy(cublas_handle,kferm,(cuComplex*) &alpha_f,(cuComplex*) p_f,1,(cuComplex*) xi_f,1);
1108#endif
1109#else
1110 for(unsigned short j=0;j<nc*ngorkov;j++)
1111 cblas_caxpy(kvol, (Complex_f*)&alpha_f,(Complex_f*)p_f+j*kvolHalo, 1, (Complex_f*)xi_f+j*kvol, 1);
1112#endif
1113#else
1114#pragma omp parallel for simd collapse(2) aligned(xi_f,p_f:AVX)
1115 for(unsigned short j=0;j<nc*ngorkov;j++)
1116 for(unsigned int i = 0; i<kvol; i++)
1117 xi_f[i+j*kvol]+=alpha*p_f[i+j*kvolHalo];
1118#endif
1119 }
1120
1121 //r=\alpha(M^\dagger)Mp and \beta_n=r*.r
1122 alignas(8) float betan_f=0;
1123#if defined USE_BLAS
1124 alignas(16) Complex_f alpha_m=(Complex_f)(-alpha);
1125#ifdef USE_GPU
1126 cublasCaxpy(cublas_handle,kferm, (cuComplex *)&alpha_m,(cuComplex *) x2_f, 1,(cuComplex *) r_f, 1);
1127 //cudaDeviceSynchronise();
1128 //r*.r
1129 cublasScnrm2(cublas_handle,kferm,(cuComplex *) r_f,1,(float *)&betan_f);
1130#else
1131 cblas_caxpy(kferm,(Complex_f*) &alpha_m,(Complex_f*) x2_f, 1,(Complex_f*) r_f, 1);
1132 //r*.r
1133 betan_f = cblas_scnrm2(kferm, (Complex_f*)r_f,1);
1134#endif
1135 //Gotta square it to "undo" the norm
1136 betan=betan_f*betan_f;
1137#else
1138 //Just like Congradq, this loop could be unrolled but will need a reduction to deal with the betan
1139 //addition.
1140 betan = 0;
1141 //If we get a small enough \beta_n before hitting the iteration cap we break
1142#pragma omp parallel for simd aligned(x2_f,r_f:AVX) reduction(+:betan)
1143 for(unsigned int i = 0; i<kferm;i++){
1144 r_f[i]-=alpha*x2_f[i];
1145 betan+=conj(r_f[i])*r_f[i];
1146 }
1147 betan_f=sqrt(betan);
1148#endif
1149 //This is basically just congradq at the end. Check there for comments
1150#if (nproc>1)
1151 Par_dsum(&betan);
1152#endif
1153 //Update beta_max if needed. On paper congrad is monotonically decreasing
1154 betan_f=sqrt(betan);
1155 beta_max = (betan_f>beta_max) ?betan_f : beta_max;
1156#ifdef _DEBUG
1157 if(!rank) printf("Iter (CG) = %i beta_n= %e alpha= %e%s", *itercg, betan, creal(alpha),endline);
1158#endif
1159 if(betan_f<beta_max*d_prec){
1160#ifdef _DEBUG
1161 if(!rank)
1162 printf("Residue %e is less than %e times %e. %s",betan_f,d_prec,beta_max,endline);
1163#endif
1164 do_dp=true;
1165 }
1166 else if(betan<resid){
1167 //Started counting from zero so add one to make it accurate
1168 (*itercg)++;
1169#ifdef _DEBUG
1170 if(!rank) printf("\nIter (CG) = %i resid = %e toler = %e\n", *itercg, betan, resid);
1171#endif
1172 //Final iteration should be in double precision.
1173 do_dp=true;
1174 }
1175 else if(*itercg==niterc-1){
1176 if(!rank) fprintf(stderr, "Warning %i in %s: Exceeded iteration limit %i beta_n=%e\n",
1177 ITERLIM, funcname, niterc, betan);
1178 ret_val=ITERLIM; break;
1179 }
1180 //Note that beta below is not the global beta and scoping is used to avoid conflict between them
1181 alignas(8) Complex beta = (*itercg) ? betan/betad : 0;
1182 betad=betan; alphan=betan;
1183 //BLAS for p=r+\betap doesn't exist in standard BLAS. This is NOT an axpy case as we're multiplying y by
1184 //\beta instead of x.
1185 //There is cblas_zaxpby in the MKL though, set a = 1 and b = \beta.
1186#ifdef USE_BLAS
1187 alignas(8) Complex_f beta_f = (Complex_f)beta;
1188 alignas(8) Complex_f a = 1.0;
1189#ifdef USE_GPU
1190#if (nproc>1) //strided
1191 for(unsigned short j=0;j<nc*ngorkov;j++){
1192 cublasCscal(cublas_handle,kvol,(cuComplex *)&beta_f,(cuComplex *)p_f+j*kvolHalo,1);
1193 cublasCaxpy(cublas_handle,kvol,(cuComplex *)&a,(cuComplex *)r_f+j*kvol,1,(cuComplex *)p_f+j*kvolHalo,1);
1194 }
1195#else
1196 cublasCscal(cublas_handle,kferm,(cuComplex *)&beta_f,(cuComplex *)p_f,1);
1197 cublasCaxpy(cublas_handle,kferm,(cuComplex *)&a,(cuComplex *)r_f,1,(cuComplex *)p_f,1);
1198#endif
1200#elif (defined __USE_MKL__||defined OPENBLAS||defined AMD_BLAS)
1201 for(unsigned short j=0;j<nc*ngorkov;j++)
1202 cblas_caxpby(kvol, &a, r_f+j*kvol, 1, &beta_f, p_f+j*kvolHalo, 1);
1203#else
1204 for(unsigned short j=0;j<nc*ngorkov;j++){
1205 cblas_cscal(kvol,&beta_f,p_f+j*kvolHalo,1);
1206 cblas_caxpy(kvol,&a,r_f+j*kvol,1,p_f+j*kvolHalo,1);
1207 }
1208#endif
1209#else
1210#pragma omp parallel for simd aligned(r_f,p_f:AVX)
1211 for(unsigned short j=0;j<nc*ngorkov;j++)
1212 for(unsigned int i=0; i<kvol; i++)
1213 p_f[i+j*kvolHalo]=r_f[i+j*kvol]+beta*p_f[i+j*kvolHalo];
1214#endif
1215 }
1216 }
1217#ifdef USE_GPU
1219#endif
1220 P_free_f(&p_f,&r_f,&x1_f,&x2_f,&xi_f);
1221 P_free(&p,&r,&x1,&x2,clover);
1222 return ret_val;
1223}
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:287
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:329
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:373
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:448
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:567
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:784
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:346
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:250
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:133
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:686
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:780
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:283
#define nc
Colours.
Definition sizes.h:186
#define ngorkov
Gor'kov indices.
Definition sizes.h:194
#define kferm2Halo
Dirac lattice and halo.
Definition sizes.h:242
#define niterc
Hard limit for runaway trajectories in Conjugate gradient.
Definition sizes.h:176
#define kvol
Sublattice volume.
Definition sizes.h:167
#define Complex
Double precision complex number.
Definition sizes.h:68
#define kferm
sublattice size including Gor'kov indices
Definition sizes.h:199
#define ndirac
Dirac indices.
Definition sizes.h:190
#define cudaDeviceSynchronise()
Get rid of that bastardised yankee English.
Definition sizes.h:57
cublasHandle_t cublas_handle
Handle for cuBLAS.
Definition main.c:47
#define Complex_f
Single precision complex number.
Definition sizes.h:66
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:201
#define kvolHalo
Subvolume + halo size.
Definition sizes.h:238
#define kfermHalo
Gor'kov lattice and halo.
Definition sizes.h:240
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.