16#ifndef PECLET_CORE_SOLVER_GRAPH_AMG_DEVICE_HPP
17#define PECLET_CORE_SOLVER_GRAPH_AMG_DEVICE_HPP
19#include <Kokkos_Core.hpp>
44 Kokkos::deep_copy(l0.
b, r);
45 Kokkos::deep_copy(l0.
x, 0.0);
47 Kokkos::deep_copy(z, l0.
x);
51 Index size(
int L = 0)
const {
return lv_[(std::size_t)L].n; }
69 View<T> d(Kokkos::view_alloc(std::string(name), Kokkos::WithoutInitializing), v.size());
70 auto h = Kokkos::create_mirror_view(d);
71 for (std::size_t i = 0; i < v.size(); ++i)
73 Kokkos::deep_copy(d, h);
79 for (
const auto& hl : host_.
levels()) {
89 if (!hl.Pstart.empty()) {
96 std::vector<Index> Rstart((std::size_t)hl.nc + 1, 0), Rcol(hl.Pcol.size());
97 std::vector<double> Rval(hl.Pval.size());
98 for (
Index c : hl.Pcol)
99 ++Rstart[(std::size_t)c + 1];
100 for (std::size_t c = 0; c < (std::size_t)hl.nc; ++c)
101 Rstart[c + 1] += Rstart[c];
102 std::vector<Index> cur(Rstart.begin(), Rstart.end() - 1);
103 for (
Index i = 0; i < hl.A.n; ++i)
104 for (
Index k = hl.Pstart[(std::size_t)i]; k < hl.Pstart[(std::size_t)i + 1]; ++k) {
105 const Index c = hl.Pcol[(std::size_t)k];
106 const Index at = cur[(std::size_t)c]++;
107 Rcol[(std::size_t)at] = i;
108 Rval[(std::size_t)at] = hl.Pval[(std::size_t)k];
114 auto scratch = [n = (std::size_t)d.
n](
const char* nm) {
115 return DView(Kokkos::view_alloc(std::string(nm), Kokkos::WithoutInitializing), n);
117 d.
x = scratch(
"amgd_x");
118 d.
b = scratch(
"amgd_b");
119 d.
res = scratch(
"amgd_res");
120 d.
t0 = scratch(
"amgd_t0");
121 d.
t1 = scratch(
"amgd_t1");
122 lv_.push_back(std::move(d));
133 Kokkos::parallel_for(
134 "peclet::core::amgd_spmv", Kokkos::RangePolicy<ExecSpace>(0, lv.
n),
135 KOKKOS_LAMBDA(
const Index i) {
136 double s = diag(i) * x(i);
137 for (
Index k = start(i); k < start(i + 1); ++k)
138 s += coef(k) * x(nbr(k));
144 const DLevel& lv = lv_[(std::size_t)L];
145 if (L + 1 == (
int)lv_.size()) {
153 Kokkos::parallel_for(
154 "peclet::core::amgd_resid", Kokkos::RangePolicy<ExecSpace>(0, lv.
n),
155 KOKKOS_LAMBDA(
const Index i) { res(i) = b(i) - res(i); });
157 const DLevel& cl = lv_[(std::size_t)L + 1];
161 Kokkos::parallel_for(
162 "peclet::core::amgd_restrict", Kokkos::RangePolicy<ExecSpace>(0, cl.
n),
163 KOKKOS_LAMBDA(
const Index c) {
165 for (
Index k = Rstart(c); k < Rstart(c + 1); ++k)
166 s += Rval(k) * res(Rcol(k));
170 Kokkos::deep_copy(cl.
x, 0.0);
175 Kokkos::parallel_for(
176 "peclet::core::amgd_prolong", Kokkos::RangePolicy<ExecSpace>(0, lv.
n),
177 KOKKOS_LAMBDA(
const Index i) {
179 for (
Index k = Pstart(i); k < Pstart(i + 1); ++k)
180 s += Pval(k) * cx(Pcol(k));
190 for (
int s = 0; s < sweeps; ++s)
201 Kokkos::parallel_for(
202 "peclet::core::amgd_jacobi", Kokkos::RangePolicy<ExecSpace>(0, lv.
n),
203 KOKKOS_LAMBDA(
const Index i) { x(i) += step * invD(i) * (b(i) - res(i)); });
208 const double lam = 1.1 * lv.
lmax;
211 Kokkos::parallel_for(
212 "peclet::core::amgd_cheb_r0", Kokkos::RangePolicy<ExecSpace>(0, lv.
n),
213 KOKKOS_LAMBDA(
const Index i) { r(i) = b(i) - r(i); });
214 Kokkos::deep_copy(d, 0.0);
215 for (
int i = 1; i <= k; ++i) {
216 const double c1 = (2.0 * i - 3.0) / (2.0 * i + 1.0);
217 const double c2 = (8.0 * i - 4.0) / ((2.0 * i + 1.0) * lam);
218 Kokkos::parallel_for(
219 "peclet::core::amgd_cheb_d", Kokkos::RangePolicy<ExecSpace>(0, lv.
n),
220 KOKKOS_LAMBDA(
const Index j) {
221 d(j) = c1 * d(j) + c2 * invD(j) * r(j);
226 Kokkos::parallel_for(
227 "peclet::core::amgd_cheb_rup", Kokkos::RangePolicy<ExecSpace>(0, lv.
n),
228 KOKKOS_LAMBDA(
const Index j) { r(j) -= Ad(j); });
235 Kokkos::parallel_reduce(
236 "peclet::core::amgd_dot", Kokkos::RangePolicy<ExecSpace>(0, n),
237 KOKKOS_LAMBDA(
const Index i,
double& acc) { acc += a(i) * b(i); }, s);
251 Kokkos::parallel_for(
252 "peclet::core::amgd_cg_r0", Kokkos::RangePolicy<ExecSpace>(0, n),
253 KOKKOS_LAMBDA(
const Index i) {
257 double rr =
dot(r, r, n);
258 const double rr0 = rr;
259 const int maxit = (int)std::min<Index>(n, 200);
260 for (
int it = 0; it < maxit && rr > 1e-24 * rr0; ++it) {
262 const double pAp =
dot(p, Ap, n);
265 const double alpha = rr / pAp;
266 Kokkos::parallel_for(
267 "peclet::core::amgd_cg_up", Kokkos::RangePolicy<ExecSpace>(0, n),
268 KOKKOS_LAMBDA(
const Index i) {
269 x(i) += alpha * p(i);
270 r(i) -= alpha * Ap(i);
272 const double rrn =
dot(r, r, n);
273 const double beta = rrn / rr;
274 Kokkos::parallel_for(
275 "peclet::core::amgd_cg_p", Kokkos::RangePolicy<ExecSpace>(0, n),
276 KOKKOS_LAMBDA(
const Index i) { p(i) = r(i) + beta * p(i); });
284 std::vector<DLevel> lv_;
void jacobiSweep(const DLevel &lv) const
const GraphAMG & hostHierarchy() const
static View< T > toDev(const std::vector< T > &v, const char *name)
void coarseSolve(const DLevel &lv) const
void build(const HostCsrOp &A, const AmgParams &prm={})
Host setup + one-time device mirror. Rebuild whenever the operator changes.
static void spmv(const DLevel &lv, const DView &x, const DView &y)
static double dot(const DView &a, const DView &b, Index n)
Index size(int L=0) const
void smooth(const DLevel &lv, int sweeps) const
void apply(const DView &r, const DView &z) const
z = M⁻¹ r: one V-cycle (correction scheme) from a zero initial guess. Device views,...
void chebSweep(const DLevel &lv) const
Smoothed-aggregation AMG hierarchy usable as z = M⁻¹ r (one symmetric V-cycle from a zero initial gue...
void build(const HostCsrOp &A, const AmgParams &prm={})
const std::vector< Level > & levels() const
Kokkos::View< T *, MemSpace > View
1D device array.
std::int64_t Index
Signed index type for grids and particles (supersedes block_decomposer's long int IndxT).
int coarseSweeps
coarsest level: 0 ⇒ a near-exact CG solve; >0 ⇒ this many smoother sweeps instead (with maxLevels=1 t...
double jacobiOmega
damped-Jacobi smoother relaxation (used when chebDegree == 0)
int chebDegree
Chebyshev smoother polynomial degree; 0 ⇒ damped-Jacobi smoother.
int post
smoother sweeps per level (pre == post keeps the V-cycle symmetric)
A general assembled sparse operator in CSR form: the diagonal is stored separately and the CSR (start...