Block-Structured AMR Software Framework
Loading...
Searching...
No Matches
AMReX_SingleBoxCGSolver.H
Go to the documentation of this file.
1#ifndef AMREX_SINGLE_BOX_CG_SOLVER_H_
2#define AMREX_SINGLE_BOX_CG_SOLVER_H_
3#include <AMReX_Config.H>
4
5#if defined(AMREX_USE_CUDA) || defined(AMREX_USE_HIP)
6
7#include <AMReX_BaseFab.H>
8#include <AMReX_Print.H>
9
10#include <iomanip>
11
12namespace amrex
13{
14
15namespace detail {
17 template <typename T>
18 struct SingleBoxCGResult
19 {
20 int ret;
21 int active;
22 int niters;
23 T rnorm0;
24 T rnorm;
25 };
26}
27
44template <typename T, class LP>
45int bicgstab_solve (Box const& box, BaseFab<T>& x, BaseFab<T> const& b, LP const& lp,
46 T eps_rel, T eps_abs, int maxiter, int verbose, int& niters)
47{
48 const int ncomp = x.nComp();
49
50 enum Index {
51 i_p, i_r, i_rh, i_v, i_t, nidx
52 };
53
54 BaseFab<T> fab(box, ncomp*nidx);
55 auto const& a = fab.array();
56 auto const& sol = x.array();
57 auto const& rhs = b.const_array();
58
59 // Single buffer for everything that goes back to the host.
61 auto* resd = res_buf.data();
62
63 Gpu::DeviceVector<T> reduce_buf;
64 Gpu::DeviceVector<T> scalar_buf;
65
66 constexpr int MT = AMREX_GPU_MAX_THREADS;
67
68 const BoxIndexer indexer(box);
69
70 bool const is_big = box.numPts() > (MT*4);
71
72 if (is_big)
73 {
74 auto nblocks_long = (box.numPts() + MT - 1) / MT;
75 AMREX_ALWAYS_ASSERT(nblocks_long <=
76 Long(std::numeric_limits<unsigned int>::max()/2));
77 auto nblocks = static_cast<unsigned int>(nblocks_long);
78
79 reduce_buf.resize(nblocks*2);
80 enum ScalarIndex {
81 s_rnorm0, s_rnorm, s_rho, s_rho_1, s_alpha, s_omega, s_beta, nscalar
82 };
83 scalar_buf.resize(nscalar);
84
85 auto* reduce = reduce_buf.data();
86 auto* scalar = scalar_buf.data();
87 auto* active = &(resd->active);
88 auto const npts = indexer.numPts();
89
90 // Initialize r and rh, and write one rnorm contribution per block.
91 amrex::launch<MT>(nblocks, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
92 {
93 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
94 T rnorm = 0;
95 if (ipt < npts) {
96 auto const cell = indexer.intVect(ipt);
97 for (int n = 0; n < ncomp; ++n) {
98 T v = rhs(cell,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));
103 }
104 }
105 rnorm = Gpu::blockReduceMax<MT>(rnorm);
106 if (threadIdx.x == 0) {
107 reduce[blockIdx.x] = rnorm;
108 }
109 });
110
111 // Finish the rnorm reduction with one block and initialize solver state.
112 amrex::launch<MT>(1, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
113 {
114 T rnorm = 0;
115 for (auto iblock = threadIdx.x; iblock < nblocks; iblock += blockDim.x) {
116 rnorm = std::max(rnorm, reduce[iblock]);
117 }
118 rnorm = Gpu::blockReduceMax<MT>(rnorm);
119 if (threadIdx.x == 0) {
120 scalar[s_rnorm0] = rnorm;
121 scalar[s_rnorm] = rnorm;
122 scalar[s_rho] = 0;
123 scalar[s_rho_1] = 0;
124 scalar[s_alpha] = 0;
125 scalar[s_omega] = 0;
126 scalar[s_beta] = 0;
127 resd->ret = 0;
128 *active = !((rnorm == 0) || (rnorm < eps_abs));
129 }
130 });
131
132 int iter = 1;
133 for (; iter <= maxiter; ++iter) {
134 // rho = dot(rh,r), with one partial sum per block.
135 amrex::launch<MT>(nblocks, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
136 {
137 if (*active == 0) { return; }
138 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
139 T rho = 0;
140 if (ipt < npts) {
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));
145 }
146 }
147 rho = Gpu::blockReduceSum<MT>(rho);
148 if (threadIdx.x == 0) {
149 reduce[blockIdx.x] = rho;
150 }
151 });
152
153 amrex::launch<MT>(1, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
154 {
155 if (*active == 0) { return; }
156 T rho = 0;
157 for (auto iblock = threadIdx.x; iblock < nblocks; iblock += blockDim.x) {
158 rho += reduce[iblock];
159 }
160 rho = Gpu::blockReduceSum<MT>(rho);
161 if (threadIdx.x == 0) {
162 scalar[s_rho] = rho;
163 if (rho == 0) {
164 resd->ret = 1;
165 *active = 0;
166 } else if (iter > 1) {
167 scalar[s_beta] = (rho/scalar[s_rho_1])
168 * (scalar[s_alpha]/scalar[s_omega]);
169 }
170 }
171 });
172
173 // Update p. This is separate from applying the operator so that
174 // all p values are globally visible before lp.apply reads them.
175 amrex::launch<MT>(nblocks, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
176 {
177 if (*active == 0) { return; }
178 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
179 if (ipt < npts) {
180 auto const cell = indexer.intVect(ipt);
181 for (int n = 0; n < ncomp; ++n) {
182 if (iter == 1) {
183 a(cell,i_p*ncomp+n) = a(cell,i_r*ncomp+n);
184 } else {
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));
188 }
189 }
190 }
191 });
192
193 // Keep applying the operator separate from the reduction. Fusing them
194 // increases register pressure for complex operators such as EB with
195 // variable coefficients.
196 // v = A p.
197 amrex::launch<MT>(nblocks, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
198 {
199 if (*active == 0) { return; }
200 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
201 if (ipt < npts) {
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));
206 }
207 }
208 });
209
210 // rhTv = dot(rh,v).
211 amrex::launch<MT>(nblocks, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
212 {
213 if (*active == 0) { return; }
214 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
215 T rhTv = 0;
216 if (ipt < npts) {
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));
221 }
222 }
223 rhTv = Gpu::blockReduceSum<MT>(rhTv);
224 if (threadIdx.x == 0) {
225 reduce[blockIdx.x] = rhTv;
226 }
227 });
228
229 amrex::launch<MT>(1, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
230 {
231 if (*active == 0) { return; }
232 T rhTv = 0;
233 for (auto iblock = threadIdx.x; iblock < nblocks; iblock += blockDim.x) {
234 rhTv += reduce[iblock];
235 }
236 rhTv = Gpu::blockReduceSum<MT>(rhTv);
237 if (threadIdx.x == 0) {
238 if (rhTv != 0) {
239 scalar[s_alpha] = scalar[s_rho] / rhTv;
240 } else {
241 resd->ret = 2;
242 *active = 0;
243 }
244 }
245 });
246
247 // x += alpha*p; r -= alpha*v; rnorm = norminf(r).
248 amrex::launch<MT>(nblocks, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
249 {
250 if (*active == 0) { return; }
251 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
252 T rnorm = 0;
253 if (ipt < npts) {
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)));
259 }
260 }
261 rnorm = Gpu::blockReduceMax<MT>(rnorm);
262 if (threadIdx.x == 0) {
263 reduce[blockIdx.x] = rnorm;
264 }
265 });
266
267 amrex::launch<MT>(1, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
268 {
269 if (*active == 0) { return; }
270 T rnorm = 0;
271 for (auto iblock = threadIdx.x; iblock < nblocks; iblock += blockDim.x) {
272 rnorm = std::max(rnorm, reduce[iblock]);
273 }
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) {
278 *active = 0;
279 }
280 }
281 });
282
283 // Keep applying the operator separate from the reduction for the same
284 // register-pressure reason as above.
285 // t = A r.
286 amrex::launch<MT>(nblocks, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
287 {
288 if (*active == 0) { return; }
289 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
290 if (ipt < npts) {
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));
295 }
296 }
297 });
298
299 // t0 = dot(t,t) and t1 = dot(t,r).
300 amrex::launch<MT>(nblocks, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
301 {
302 if (*active == 0) { return; }
303 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
304 T t0 = 0;
305 T t1 = 0;
306 if (ipt < npts) {
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));
313 }
314 }
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;
320 }
321 });
322
323 amrex::launch<MT>(1, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
324 {
325 if (*active == 0) { return; }
326 T t0 = 0;
327 T t1 = 0;
328 for (auto iblock = threadIdx.x; iblock < nblocks; iblock += blockDim.x) {
329 t0 += reduce[iblock];
330 t1 += reduce[nblocks+iblock];
331 }
332 t0 = Gpu::blockReduceSum<MT>(t0);
333 t1 = Gpu::blockReduceSum<MT>(t1);
334 if (threadIdx.x == 0) {
335 if (t0 != 0) {
336 scalar[s_omega] = t1/t0;
337 } else {
338 resd->ret = 3;
339 *active = 0;
340 }
341 }
342 });
343
344 // x += omega*r; r -= omega*t; rnorm = norminf(r).
345 amrex::launch<MT>(nblocks, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
346 {
347 if (*active == 0) { return; }
348 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
349 T rnorm = 0;
350 if (ipt < npts) {
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)));
356 }
357 }
358 rnorm = Gpu::blockReduceMax<MT>(rnorm);
359 if (threadIdx.x == 0) {
360 reduce[blockIdx.x] = rnorm;
361 }
362 });
363
364 amrex::launch<MT>(1, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
365 {
366 if (*active == 0) { return; }
367 T rnorm = 0;
368 for (auto iblock = threadIdx.x; iblock < nblocks; iblock += blockDim.x) {
369 rnorm = std::max(rnorm, reduce[iblock]);
370 }
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) {
375 *active = 0;
376 } else if (scalar[s_omega] == 0) {
377 resd->ret = 4;
378 *active = 0;
379 } else {
380 scalar[s_rho_1] = scalar[s_rho];
381 }
382 }
383 });
384
385 if (res_buf.copyToHost()->active == 0) {
386 break;
387 }
388 }
389
390 // Match the return and solution-reset behavior of the one-block path.
391 amrex::launch<MT>(1, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
392 {
393 if (threadIdx.x == 0) {
394 T const rnorm = scalar[s_rnorm];
395 T const rnorm0 = scalar[s_rnorm0];
396 int ret = resd->ret;
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)
401 {
402 ret = 8;
403 }
404 keep_solution = ((ret == 0 || ret == 8) && rnorm < rnorm0);
405 if (keep_solution && ret == 8) { ret = 9; }
406 }
407 resd->ret = ret;
408 *active = keep_solution;
409 resd->rnorm0 = rnorm0;
410 resd->rnorm = rnorm;
411 }
412 });
413
414 amrex::launch<MT>(nblocks, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
415 {
416 if (*active == 0) {
417 auto const ipt = std::uint64_t(blockIdx.x)*blockDim.x + threadIdx.x;
418 if (ipt < npts) {
419 auto const cell = indexer.intVect(ipt);
420 for (int n = 0; n < ncomp; ++n) {
421 sol(cell,n) = 0;
422 }
423 }
424 }
425 });
426
427 // Like MLCGSolver, iter is maxiter+1 if the loop was exhausted. The
428 // no-iteration case is fixed up below once the norms are on the host.
429 niters = iter;
430 }
431 else
432 {
433 amrex::launch<MT>(1, Gpu::gpuStream(), [=] AMREX_GPU_DEVICE ()
434 {
435 auto npts = static_cast<unsigned int>(indexer.numPts());
436
437 T rnorm = 0;
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) {
441 T v = rhs(cell,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));
446 }
447 }
448
449 rnorm = Gpu::blockReduceMax<MT>(rnorm);
450
451 __shared__ T shared_scalar[2];
452
453 if (threadIdx.x == 0) { shared_scalar[0] = rnorm; }
454 __syncthreads();
455 T const rnorm0 = shared_scalar[0];
456
457 if (rnorm0 == 0 || rnorm0 < eps_abs) {
458 if (threadIdx.x == 0) {
459 resd->ret = 0;
460 resd->niters = 0;
461 resd->rnorm0 = rnorm0;
462 resd->rnorm = rnorm0;
463 }
464 return;
465 }
466
467 int ret = 0;
468 T rho_1 = 0, alpha = 0, omega = 0;
469 int iter = 1;
470 for (; iter <= maxiter; ++iter) {
471 T rho = 0;
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));
476 }
477 }
478 rho = Gpu::blockReduceSum<MT>(rho);
479
480 if (threadIdx.x == 0) { shared_scalar[0] = rho; }
481 __syncthreads();
482 rho = shared_scalar[0];
483
484 if (rho == 0) {
485 ret = 1;
486 break;
487 }
488
489 if (iter == 1) {
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);
494 }
495 }
496 } else {
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));
503 }
504 }
505 }
506
507 __syncthreads(); // needed for lp.apply
508
509 T rhTv = 0;
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));
516 }
517 }
518 rhTv = Gpu::blockReduceSum<MT>(rhTv);
519
520 if (threadIdx.x == 0) { shared_scalar[0] = rhTv; }
521 __syncthreads();
522 rhTv = shared_scalar[0];
523
524 if (rhTv != 0) {
525 alpha = rho / rhTv;
526 } else {
527 ret = 2;
528 break;
529 }
530
531 rnorm = 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)));
538 }
539 }
540 rnorm = Gpu::blockReduceMax<MT>(rnorm);
541
542 if (threadIdx.x == 0) { shared_scalar[0] = rnorm; }
543 __syncthreads();
544 rnorm = shared_scalar[0];
545
546 if ( rnorm < eps_rel*rnorm0 || rnorm < eps_abs ) { break; }
547
548 T t0 = 0, t1 = 0;
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));
556 }
557 }
558 t0 = Gpu::blockReduceSum<MT>(t0);
559 t1 = Gpu::blockReduceSum<MT>(t1);
560
561 if (threadIdx.x == 0) {
562 shared_scalar[0] = t0;
563 shared_scalar[1] = t1;
564 }
565 __syncthreads();
566 t0 = shared_scalar[0];
567 t1 = shared_scalar[1];
568
569 if (t0 != 0) {
570 omega = t1 / t0;
571 } else {
572 ret = 3;
573 break;
574 }
575
576 rnorm = 0;
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)));
583 }
584 }
585 rnorm = Gpu::blockReduceMax<MT>(rnorm);
586
587 if (threadIdx.x == 0) { shared_scalar[0] = rnorm; }
588 __syncthreads();
589 rnorm = shared_scalar[0];
590
591 if ( rnorm < eps_rel*rnorm0 || rnorm < eps_abs ) { break; }
592
593 if ( omega == 0 ) {
594 ret = 4;
595 break;
596 }
597 rho_1 = rho;
598 }
599
600 if ( ret == 0 && rnorm > eps_rel*rnorm0 && rnorm > eps_abs) {
601 ret = 8;
602 }
603
604 if ( ( ret == 0 || ret == 8 ) && (rnorm < rnorm0) ) {
605 if (ret == 8) { ret = 9; }
606 } else {
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) {
610 sol(cell,n) = 0;
611 }
612 }
613 }
614
615 if (threadIdx.x == 0) {
616 resd->ret = ret;
617 resd->niters = iter;
618 resd->rnorm0 = rnorm0;
619 resd->rnorm = rnorm;
620 }
621 });
622 }
623
624 auto const* resh = res_buf.copyToHost(); // this has stream sync.
625 int const ret = resh->ret;
626 T const rnorm0 = resh->rnorm0;
627 T const rnorm = resh->rnorm;
628 if (is_big) {
629 if (rnorm0 == 0 || rnorm0 < eps_abs) { niters = 0; }
630 } else {
631 niters = resh->niters;
632 }
633
634 // Host-side output only, mirroring MLCGSolver. Only the rank that owns
635 // the box gets here, so AllPrint is used instead of Print.
636 if (verbose > 0) {
637 amrex::AllPrint() << "SingleBoxCGSolver_BiCGStab: Initial error (error0) = "
638 << rnorm0 << '\n';
639 if (niters == 0) {
640 amrex::AllPrint() << "SingleBoxCGSolver_BiCGStab: niter = 0, rnorm = " << rnorm
641 << ", eps_abs = " << eps_abs << '\n';
642 } else {
643 amrex::AllPrint() << "SingleBoxCGSolver_BiCGStab: Final: Iteration "
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!");
648 }
649 }
650 }
651
652 return ret;
653}
654
655}
656
657#endif
658#endif
#define AMREX_ALWAYS_ASSERT(EX)
Definition AMReX_BLassert.H:50
BaseFab container template providing box-based field storage.
#define AMREX_GPU_DEVICE
Definition AMReX_GpuQualifiers.H:18
GpuArray< Real, 3 > beta
Definition AMReX_MLEBNodeFDLaplacian.cpp:1099
Print on all processors of the default communicator.
Definition AMReX_Print.H:113
A FortranArrayBox(FAB)-like object.
Definition AMReX_BaseFab.H:222
Array4< T const > const_array() const noexcept
Return a const-qualified Array4 view over all components.
Definition AMReX_BaseFab.H:547
Array4< T const > array() const noexcept
Create an Array4 view over all components.
Definition AMReX_BaseFab.H:475
__host__ __device__ Long numPts() const noexcept
Return the number of points contained in the BoxND.
Definition AMReX_Box.H:385
Definition AMReX_GpuBuffer.H:24
T const * data() const noexcept
Definition AMReX_GpuBuffer.H:51
T * copyToHost()
Definition AMReX_GpuBuffer.H:151
Dynamically allocated vector for trivially copyable data.
Definition AMReX_PODVector.H:308
void resize(size_type a_new_size, GrowthStrategy strategy=GrowthStrategy::Poisson)
Definition AMReX_PODVector.H:734
T * data() noexcept
Definition AMReX_PODVector.H:672
amrex_long Long
Definition AMReX_INT.H:30
gpuStream_t gpuStream() noexcept
Definition AMReX_GpuDevice.H:291
Definition AMReX_Amr.cpp:50
int bicgstab_solve(Box const &box, BaseFab< T > &x, BaseFab< T > const &b, LP const &lp, T eps_rel, T eps_abs, int maxiter, int verbose, int &niters)
BiCGStab solver for a single Box that runs entirely on the GPU.
Definition AMReX_SingleBoxCGSolver.H:45
void Warning(const std::string &msg)
Print a warning message to the diagnostic stream and keep running.
Definition AMReX.cpp:248
Utility that maps flattened point indices back to IntVectND coordinates.
Definition AMReX_Box.H:2494
__host__ __device__ IntVectND< dim > intVect(std::uint64_t icell) const
Convert flattened point index icell to its IntVectND coordinate.
Definition AMReX_Box.H:2517
__host__ __device__ std::uint64_t numPts() const
Return the number of points covered by the indexed box.
Definition AMReX_Box.H:2552