46 T eps_rel, T eps_abs,
int maxiter,
int verbose,
int& niters)
48 const int ncomp =
x.nComp();
51 i_p, i_r, i_rh, i_v, i_t, nidx
55 auto const& a = fab.
array();
56 auto const& sol =
x.array();
61 auto* resd = res_buf.
data();
66 constexpr int MT = AMREX_GPU_MAX_THREADS;
70 bool const is_big = box.
numPts() > (MT*4);
74 auto nblocks_long = (box.
numPts() + MT - 1) / MT;
76 Long(std::numeric_limits<unsigned int>::max()/2));
77 auto nblocks =
static_cast<unsigned int>(nblocks_long);
79 reduce_buf.
resize(nblocks*2);
81 s_rnorm0, s_rnorm, s_rho, s_rho_1, s_alpha, s_omega, s_beta, nscalar
83 scalar_buf.
resize(nscalar);
85 auto* reduce = reduce_buf.
data();
86 auto* scalar = scalar_buf.
data();
87 auto* active = &(resd->active);
88 auto const npts = indexer.
numPts();
93 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
96 auto const cell = indexer.
intVect(ipt);
97 for (
int n = 0; n < ncomp; ++n) {
99 lp.normalize(cell, n, v);
100 a(cell,i_r *ncomp+n) = v;
101 a(cell,i_rh*ncomp+n) = v;
102 rnorm = std::max(rnorm, std::abs(v));
105 rnorm = Gpu::blockReduceMax<MT>(rnorm);
106 if (threadIdx.x == 0) {
107 reduce[blockIdx.x] = rnorm;
115 for (
auto iblock = threadIdx.x; iblock < nblocks; iblock += blockDim.x) {
116 rnorm = std::max(rnorm, reduce[iblock]);
118 rnorm = Gpu::blockReduceMax<MT>(rnorm);
119 if (threadIdx.x == 0) {
120 scalar[s_rnorm0] = rnorm;
121 scalar[s_rnorm] = rnorm;
128 *active = !((rnorm == 0) || (rnorm < eps_abs));
133 for (; iter <= maxiter; ++iter) {
137 if (*active == 0) {
return; }
138 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
141 auto const cell = indexer.
intVect(ipt);
142 for (
int n = 0; n < ncomp; ++n) {
143 rho += lp.xdoty(cell, n, a(cell,i_rh*ncomp+n),
144 a(cell,i_r*ncomp+n));
147 rho = Gpu::blockReduceSum<MT>(rho);
148 if (threadIdx.x == 0) {
149 reduce[blockIdx.x] = rho;
155 if (*active == 0) {
return; }
157 for (
auto iblock = threadIdx.x; iblock < nblocks; iblock += blockDim.x) {
158 rho += reduce[iblock];
160 rho = Gpu::blockReduceSum<MT>(rho);
161 if (threadIdx.x == 0) {
166 }
else if (iter > 1) {
167 scalar[s_beta] = (rho/scalar[s_rho_1])
168 * (scalar[s_alpha]/scalar[s_omega]);
177 if (*active == 0) {
return; }
178 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
180 auto const cell = indexer.
intVect(ipt);
181 for (
int n = 0; n < ncomp; ++n) {
183 a(cell,i_p*ncomp+n) = a(cell,i_r*ncomp+n);
185 a(cell,i_p*ncomp+n) = a(cell,i_r*ncomp+n)
186 + scalar[s_beta] * (a(cell,i_p*ncomp+n)
187 - scalar[s_omega]*a(cell,i_v*ncomp+n));
199 if (*active == 0) {
return; }
200 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
202 auto const cell = indexer.
intVect(ipt);
203 for (
int n = 0; n < ncomp; ++n) {
204 a(cell,i_v*ncomp+n) = lp.apply(cell, n, a, i_p*ncomp+n);
205 lp.normalize(cell, n, a(cell,i_v*ncomp+n));
213 if (*active == 0) {
return; }
214 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
217 auto const cell = indexer.
intVect(ipt);
218 for (
int n = 0; n < ncomp; ++n) {
219 rhTv += lp.xdoty(cell, n, a(cell,i_rh*ncomp+n),
220 a(cell,i_v*ncomp+n));
223 rhTv = Gpu::blockReduceSum<MT>(rhTv);
224 if (threadIdx.x == 0) {
225 reduce[blockIdx.x] = rhTv;
231 if (*active == 0) {
return; }
233 for (
auto iblock = threadIdx.x; iblock < nblocks; iblock += blockDim.x) {
234 rhTv += reduce[iblock];
236 rhTv = Gpu::blockReduceSum<MT>(rhTv);
237 if (threadIdx.x == 0) {
239 scalar[s_alpha] = scalar[s_rho] / rhTv;
250 if (*active == 0) {
return; }
251 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
254 auto const cell = indexer.
intVect(ipt);
255 for (
int n = 0; n < ncomp; ++n) {
256 sol(cell,n) += scalar[s_alpha]*a(cell,i_p*ncomp+n);
257 a(cell,i_r*ncomp+n) -= scalar[s_alpha]*a(cell,i_v*ncomp+n);
258 rnorm = std::max(rnorm, std::abs(a(cell,i_r*ncomp+n)));
261 rnorm = Gpu::blockReduceMax<MT>(rnorm);
262 if (threadIdx.x == 0) {
263 reduce[blockIdx.x] = rnorm;
269 if (*active == 0) {
return; }
271 for (
auto iblock = threadIdx.x; iblock < nblocks; iblock += blockDim.x) {
272 rnorm = std::max(rnorm, reduce[iblock]);
274 rnorm = Gpu::blockReduceMax<MT>(rnorm);
275 if (threadIdx.x == 0) {
276 scalar[s_rnorm] = rnorm;
277 if (rnorm < eps_rel*scalar[s_rnorm0] || rnorm < eps_abs) {
288 if (*active == 0) {
return; }
289 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
291 auto const cell = indexer.
intVect(ipt);
292 for (
int n = 0; n < ncomp; ++n) {
293 a(cell,i_t*ncomp+n) = lp.apply(cell, n, a, i_r*ncomp+n);
294 lp.normalize(cell, n, a(cell,i_t*ncomp+n));
302 if (*active == 0) {
return; }
303 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
307 auto const cell = indexer.
intVect(ipt);
308 for (
int n = 0; n < ncomp; ++n) {
309 t0 += lp.xdoty(cell, n, a(cell,i_t*ncomp+n),
310 a(cell,i_t*ncomp+n));
311 t1 += lp.xdoty(cell, n, a(cell,i_t*ncomp+n),
312 a(cell,i_r*ncomp+n));
315 t0 = Gpu::blockReduceSum<MT>(t0);
316 t1 = Gpu::blockReduceSum<MT>(t1);
317 if (threadIdx.x == 0) {
318 reduce[blockIdx.x] = t0;
319 reduce[nblocks+blockIdx.x] = t1;
325 if (*active == 0) {
return; }
328 for (
auto iblock = threadIdx.x; iblock < nblocks; iblock += blockDim.x) {
329 t0 += reduce[iblock];
330 t1 += reduce[nblocks+iblock];
332 t0 = Gpu::blockReduceSum<MT>(t0);
333 t1 = Gpu::blockReduceSum<MT>(t1);
334 if (threadIdx.x == 0) {
336 scalar[s_omega] = t1/t0;
347 if (*active == 0) {
return; }
348 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
351 auto const cell = indexer.
intVect(ipt);
352 for (
int n = 0; n < ncomp; ++n) {
353 sol(cell,n) += scalar[s_omega]*a(cell,i_r*ncomp+n);
354 a(cell,i_r*ncomp+n) -= scalar[s_omega]*a(cell,i_t*ncomp+n);
355 rnorm = std::max(rnorm, std::abs(a(cell,i_r*ncomp+n)));
358 rnorm = Gpu::blockReduceMax<MT>(rnorm);
359 if (threadIdx.x == 0) {
360 reduce[blockIdx.x] = rnorm;
366 if (*active == 0) {
return; }
368 for (
auto iblock = threadIdx.x; iblock < nblocks; iblock += blockDim.x) {
369 rnorm = std::max(rnorm, reduce[iblock]);
371 rnorm = Gpu::blockReduceMax<MT>(rnorm);
372 if (threadIdx.x == 0) {
373 scalar[s_rnorm] = rnorm;
374 if (rnorm < eps_rel*scalar[s_rnorm0] || rnorm < eps_abs) {
376 }
else if (scalar[s_omega] == 0) {
380 scalar[s_rho_1] = scalar[s_rho];
393 if (threadIdx.x == 0) {
394 T const rnorm = scalar[s_rnorm];
395 T const rnorm0 = scalar[s_rnorm0];
397 bool keep_solution = (rnorm0 == 0 || rnorm0 < eps_abs);
398 if (!keep_solution) {
399 if (*active != 0 && ret == 0 &&
400 rnorm > eps_rel*rnorm0 && rnorm > eps_abs)
404 keep_solution = ((ret == 0 || ret == 8) && rnorm < rnorm0);
405 if (keep_solution && ret == 8) { ret = 9; }
408 *active = keep_solution;
409 resd->rnorm0 = rnorm0;
417 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
419 auto const cell = indexer.
intVect(ipt);
420 for (
int n = 0; n < ncomp; ++n) {
435 auto npts =
static_cast<unsigned int>(indexer.numPts());
438 for (
auto ipt = threadIdx.x; ipt < npts; ipt += blockDim.x) {
439 auto cell = indexer.intVect(ipt);
440 for (
int n = 0; n < ncomp; ++n) {
442 lp.normalize(cell, n, v);
443 a(cell,i_r *ncomp+n) = v;
444 a(cell,i_rh*ncomp+n) = v;
445 rnorm = std::max(rnorm, std::abs(v));
449 rnorm = Gpu::blockReduceMax<MT>(rnorm);
451 __shared__ T shared_scalar[2];
453 if (threadIdx.x == 0) { shared_scalar[0] = rnorm; }
455 T
const rnorm0 = shared_scalar[0];
457 if (rnorm0 == 0 || rnorm0 < eps_abs) {
458 if (threadIdx.x == 0) {
461 resd->rnorm0 = rnorm0;
462 resd->rnorm = rnorm0;
468 T rho_1 = 0, alpha = 0, omega = 0;
470 for (; iter <= maxiter; ++iter) {
472 for (
auto ipt = threadIdx.x; ipt < npts; ipt += blockDim.x) {
473 auto cell = indexer.intVect(ipt);
474 for (
int n = 0; n < ncomp; ++n) {
475 rho += lp.xdoty(cell, n, a(cell,i_rh*ncomp+n), a(cell,i_r*ncomp+n));
478 rho = Gpu::blockReduceSum<MT>(rho);
480 if (threadIdx.x == 0) { shared_scalar[0] = rho; }
482 rho = shared_scalar[0];
490 for (
auto ipt = threadIdx.x; ipt < npts; ipt += blockDim.x) {
491 auto cell = indexer.intVect(ipt);
492 for (
int n = 0; n < ncomp; ++n) {
493 a(cell,i_p*ncomp+n) = a(cell,i_r*ncomp+n);
497 T
const beta = (rho/rho_1)*(alpha/omega);
498 for (
auto ipt = threadIdx.x; ipt < npts; ipt += blockDim.x) {
499 auto cell = indexer.intVect(ipt);
500 for (
int n = 0; n < ncomp; ++n) {
501 a(cell,i_p*ncomp+n) = a(cell,i_r*ncomp+n)
502 +
beta * (a(cell,i_p*ncomp+n) - omega*a(cell,i_v*ncomp+n));
510 for (
auto ipt = threadIdx.x; ipt < npts; ipt += blockDim.x) {
511 auto cell = indexer.intVect(ipt);
512 for (
int n = 0; n < ncomp; ++n) {
513 a(cell,i_v*ncomp+n) = lp.apply(cell, n, a, i_p*ncomp+n);
514 lp.normalize(cell, n, a(cell,i_v*ncomp+n));
515 rhTv += lp.xdoty(cell, n, a(cell,i_rh*ncomp+n), a(cell,i_v*ncomp+n));
518 rhTv = Gpu::blockReduceSum<MT>(rhTv);
520 if (threadIdx.x == 0) { shared_scalar[0] = rhTv; }
522 rhTv = shared_scalar[0];
532 for (
auto ipt = threadIdx.x; ipt < npts; ipt += blockDim.x) {
533 auto cell = indexer.intVect(ipt);
534 for (
int n = 0; n < ncomp; ++n) {
535 sol(cell,n) += alpha * a(cell,i_p*ncomp+n);
536 a(cell,i_r*ncomp+n) -= alpha * a(cell,i_v*ncomp+n);
537 rnorm = std::max(rnorm, std::abs(a(cell,i_r*ncomp+n)));
540 rnorm = Gpu::blockReduceMax<MT>(rnorm);
542 if (threadIdx.x == 0) { shared_scalar[0] = rnorm; }
544 rnorm = shared_scalar[0];
546 if ( rnorm < eps_rel*rnorm0 || rnorm < eps_abs ) {
break; }
549 for (
auto ipt = threadIdx.x; ipt < npts; ipt += blockDim.x) {
550 auto cell = indexer.intVect(ipt);
551 for (
int n = 0; n < ncomp; ++n) {
552 a(cell,i_t*ncomp+n) = lp.apply(cell, n, a, i_r*ncomp+n);
553 lp.normalize(cell, n, a(cell,i_t*ncomp+n));
554 t0 += lp.xdoty(cell, n, a(cell,i_t*ncomp+n), a(cell,i_t*ncomp+n));
555 t1 += lp.xdoty(cell, n, a(cell,i_t*ncomp+n), a(cell,i_r*ncomp+n));
558 t0 = Gpu::blockReduceSum<MT>(t0);
559 t1 = Gpu::blockReduceSum<MT>(t1);
561 if (threadIdx.x == 0) {
562 shared_scalar[0] = t0;
563 shared_scalar[1] = t1;
566 t0 = shared_scalar[0];
567 t1 = shared_scalar[1];
577 for (
auto ipt = threadIdx.x; ipt < npts; ipt += blockDim.x) {
578 auto cell = indexer.intVect(ipt);
579 for (
int n = 0; n < ncomp; ++n) {
580 sol(cell,n) += omega * a(cell,i_r*ncomp+n);
581 a(cell,i_r*ncomp+n) -= omega * a(cell,i_t*ncomp+n);
582 rnorm = std::max(rnorm, std::abs(a(cell,i_r*ncomp+n)));
585 rnorm = Gpu::blockReduceMax<MT>(rnorm);
587 if (threadIdx.x == 0) { shared_scalar[0] = rnorm; }
589 rnorm = shared_scalar[0];
591 if ( rnorm < eps_rel*rnorm0 || rnorm < eps_abs ) {
break; }
600 if ( ret == 0 && rnorm > eps_rel*rnorm0 && rnorm > eps_abs) {
604 if ( ( ret == 0 || ret == 8 ) && (rnorm < rnorm0) ) {
605 if (ret == 8) { ret = 9; }
607 for (
auto ipt = threadIdx.x; ipt < npts; ipt += blockDim.x) {
608 auto cell = indexer.intVect(ipt);
609 for (
int n = 0; n < ncomp; ++n) {
615 if (threadIdx.x == 0) {
618 resd->rnorm0 = rnorm0;
624 auto const* resh = res_buf.copyToHost();
625 int const ret = resh->ret;
626 T
const rnorm0 = resh->rnorm0;
627 T
const rnorm = resh->rnorm;
629 if (rnorm0 == 0 || rnorm0 < eps_abs) { niters = 0; }
631 niters = resh->niters;
637 amrex::AllPrint() <<
"SingleBoxCGSolver_BiCGStab: Initial error (error0) = "
640 amrex::AllPrint() <<
"SingleBoxCGSolver_BiCGStab: niter = 0, rnorm = " << rnorm
641 <<
", eps_abs = " << eps_abs <<
'\n';
644 << std::setw(4) << niters
645 <<
" rel. err. " << rnorm/rnorm0 <<
'\n';
646 if (ret == 8 || ret == 9) {
647 amrex::Warning(
"SingleBoxCGSolver_BiCGStab:: failed to converge!");