64 const std::vector<Real>& vsetIn,
const Real L[3],
int N,
int sw,
65 const Sdf& sdf,
int maxNewton, Real tol,
int cgIters = 300,
67 Real muBarrier = 0, Real muDecay = (Real)0.7,
bool freeEnergy =
false) {
68 using peclet::core::Index;
69 using MemSpace = peclet::core::MemSpace;
70 const Real Larr[3] = {L[0], L[1], L[2]};
71 constexpr bool kHasSdf = !std::is_same_v<Sdf, peclet::voro::NoSdf>;
72 const int nD = Weighted ? 4 * N : 3 * N;
73 auto wdof = [N](
int i) {
return 3 * N + i; };
75 Kokkos::View<long*, MemSpace> gd;
76 const double boxVol = (double)L[0] * L[1] * L[2];
78 for (
int i = 0; i < N; ++i) sumVset += vsetIn[i];
79 std::vector<double> vset(N);
80 for (
int i = 0; i < N; ++i) vset[i] = vsetIn[i] * (boxVol / sumVset);
87 const double barEps = 0.1;
88 auto barCell = [&](
double V,
double vref,
double mu) {
89 struct {
double e, d1, d2; } r{0.0, 0.0, 0.0};
90 if (mu <= 0.0)
return r;
91 const double t = V / vref;
93 r.e = -mu * std::log(t);
97 const double dt = t - barEps, ie = 1.0 / barEps;
98 r.e = mu * (-std::log(barEps) - dt * ie + 0.5 * dt * dt * ie * ie);
99 r.d1 = mu * (-ie + dt * ie * ie) / vref;
100 r.d2 = mu * ie * ie / (vref * vref);
104 const double feMin = 0.1;
105 auto freeCell = [&](
double V,
double vref) {
106 struct {
double e, d1, d2; } r{0.0, 0.0, 0.0};
107 const double Vmin = feMin * vref, iv = 1.0 / Vmin;
109 r.e = -vref * std::log(V);
111 r.d2 = vref / (V * V);
113 const double dV = V - Vmin;
114 r.e = -vref * (std::log(Vmin) + dV * iv - 0.5 * dV * dV * iv * iv);
115 r.d1 = -vref * (iv - dV * iv * iv);
116 r.d2 = vref * iv * iv;
122 std::vector<double> vol, dvr, area, conn;
123 std::vector<int> off, cnt;
124 std::vector<peclet::voro::gid_t> nbr;
125 double E = 0, maxErr = 0, meanErr = 0;
128 auto build = [&](
const std::vector<Real>& x,
const std::vector<Real>& w, Geo& G) {
129 Kokkos::View<Real*, MemSpace> dpos(
"mo.pos", 3 * N), dw;
130 Kokkos::deep_copy(dpos, Kokkos::View<const Real*, Kokkos::HostSpace>(x.data(), 3 * N));
131 if constexpr (Weighted) {
132 dw = Kokkos::View<Real*, MemSpace>(
"mo.w", N);
133 Kokkos::deep_copy(dw, Kokkos::View<const Real*, Kokkos::HostSpace>(w.data(), N));
135 auto res = buildTessellation<Real, Weighted, Sdf>(dpos, dw, N, Larr, sw, N, gd, sdf,
true);
143 G.E = G.maxErr = G.meanErr = 0;
145 for (
int i = 0; i < N; ++i) {
146 const double e = G.vol[i] / vset[i] - 1.0;
147 G.E += freeEnergy ? freeCell(G.vol[i], vset[i]).e : e * e;
148 G.maxErr = std::max(G.maxErr, std::fabs(e));
149 G.meanErr += std::fabs(e);
150 if (G.vol[i] <= 0.0) ++G.nBad;
157 auto stencil = [&](
const Geo& G,
int c, std::vector<std::pair<int, double>>& st) {
159 const int nF = (int)G.nbr.size();
160 double sx = 0, sy = 0, sz = 0, sw2 = 0;
161 for (
int f = G.off[c]; f < G.off[c] + G.cnt[c] && f < nF; ++f) {
162 const long j = (long)G.nbr[f];
163 if (j < 0 || j >= N)
continue;
164 const double dx = G.dvr[3 * f], dy = G.dvr[3 * f + 1], dz = G.dvr[3 * f + 2];
165 st.emplace_back(3 * (
int)j, dx);
166 st.emplace_back(3 * (
int)j + 1, dy);
167 st.emplace_back(3 * (
int)j + 2, dz);
171 if constexpr (Weighted) {
172 const double A = std::sqrt(G.area[3 * f] * G.area[3 * f] +
173 G.area[3 * f + 1] * G.area[3 * f + 1] +
174 G.area[3 * f + 2] * G.area[3 * f + 2]);
175 const double d = std::sqrt(G.conn[3 * f] * G.conn[3 * f] +
176 G.conn[3 * f + 1] * G.conn[3 * f + 1] +
177 G.conn[3 * f + 2] * G.conn[3 * f + 2]);
178 const double wgt = (d > 0) ? A / (2 * d) : 0;
179 st.emplace_back(wdof((
int)j), -wgt);
190 if constexpr (kHasSdf) {
192 double gw[3] = {0, 0, 0};
193 for (
int f = G.off[c]; f < G.off[c] + G.cnt[c] && f < nF; ++f)
195 gw[0] += G.area[3 * f];
196 gw[1] += G.area[3 * f + 1];
197 gw[2] += G.area[3 * f + 2];
199 if (gw[0] != 0.0 || gw[1] != 0.0 || gw[2] != 0.0) {
201 sdfGradient<Real>(sdf, pos[3 * c], pos[3 * c + 1], pos[3 * c + 2], gg);
202 const double gn = std::sqrt((
double)gg[0] * gg[0] + (
double)gg[1] * gg[1] + (
double)gg[2] * gg[2]);
204 const double u[3] = {gg[0] / gn, gg[1] / gn, gg[2] / gn};
205 const double ug = u[0] * gw[0] + u[1] * gw[1] + u[2] * gw[2];
206 const double perp[3] = {gw[0] - ug * u[0], gw[1] - ug * u[1], gw[2] - ug * u[2]};
208 sdfHessian<Real>(sdf, pos[3 * c], pos[3 * c + 1], pos[3 * c + 2], Hm);
209 const double Hp[3] = {Hm[0][0] * perp[0] + Hm[0][1] * perp[1] + Hm[0][2] * perp[2],
210 Hm[1][0] * perp[0] + Hm[1][1] * perp[1] + Hm[1][2] * perp[2],
211 Hm[2][0] * perp[0] + Hm[2][1] * perp[1] + Hm[2][2] * perp[2]};
216 const double cH = (double)sdf.eval(pos[3 * c], pos[3 * c + 1], pos[3 * c + 2]) / gn;
217 sx += -ug * u[0] - cH * Hp[0];
218 sy += -ug * u[1] - cH * Hp[1];
219 sz += -ug * u[2] - cH * Hp[2];
223 st.emplace_back(3 * c, sx);
224 st.emplace_back(3 * c + 1, sy);
225 st.emplace_back(3 * c + 2, sz);
226 if constexpr (Weighted) st.emplace_back(wdof(c), sw2);
230 build(pos, weight, G);
237 double totVol = 0.0, sVset = 0.0;
238 for (
int i = 0; i < N; ++i) {
242 const double sc = (sVset > 0.0) ? totVol / sVset : 1.0;
243 for (
int i = 0; i < N; ++i) vset[i] *= sc;
244 G.E = G.maxErr = G.meanErr = 0;
246 for (
int i = 0; i < N; ++i) {
247 const double e = G.vol[i] / vset[i] - 1.0;
248 G.E += freeEnergy ? freeCell(G.vol[i], vset[i]).e : e * e;
249 G.maxErr = std::max(G.maxErr, std::fabs(e));
250 G.meanErr += std::fabs(e);
251 if (G.vol[i] <= 0.0) ++G.nBad;
255 auto pack = [&](
const std::vector<Real>& x,
const std::vector<Real>& w, std::vector<Real>& q) {
256 q.assign(x.begin(), x.end());
257 if constexpr (Weighted) q.insert(q.end(), w.begin(), w.end());
259 auto unpack = [&](
const std::vector<Real>& q, std::vector<Real>& x, std::vector<Real>& w) {
260 x.assign(q.begin(), q.begin() + 3 * N);
261 if constexpr (Weighted) w.assign(q.begin() + 3 * N, q.end());
264 auto barrierE = [&](
const Geo& G,
double mu) ->
double {
265 if (freeEnergy || mu <= 0.0)
return 0.0;
267 for (
int i = 0; i < N; ++i) b += barCell(G.vol[i], vset[i], mu).e;
272 std::vector<double> g(nD), dq(nD);
273 std::vector<std::pair<int, double>> st;
275 for (
int it = 0; it < maxNewton; ++it) {
278 const double muCur = freeEnergy ? 0.0 : (double)muBarrier * std::pow((
double)muDecay, it);
280 std::fill(g.begin(), g.end(), 0.0);
281 std::vector<std::unordered_map<int, double>> row(nD);
282 for (
int c = 0; c < N; ++c) {
290 const auto fc = freeCell(G.vol[c], vset[c]);
294 Rc = 2.0 * (G.vol[c] / vset[c] - 1.0) / vset[c];
295 Hw = 2.0 / (vset[c] * vset[c]);
297 const auto bc = barCell(G.vol[c], vset[c], muCur);
303 for (
auto& [a, va] : st) {
307 for (
auto& [b, vb] : st) ra[b] += Hw * va * vb;
312 for (
int i = 0; i < nD; ++i) gnorm = std::max(gnorm, std::fabs(g[i]));
314 if (verbose && it == 0) {
315 const int d = 3 * (N / 2);
316 const double h = 1e-7;
317 std::vector<Real> xp = pos, wp = weight;
321 xp[d] -= (Real)(2 * h);
323 std::printf(
" FD dE/dx_%d: analytic=%.4e fd=%.4e\n", d, g[d],
324 ((Gp.E + barrierE(Gp, muCur)) - (Gm.E + barrierE(Gm, muCur))) / (2 * h));
325 if constexpr (Weighted) {
326 const int dw = wdof(N / 3);
327 std::vector<Real> wq = weight;
328 wq[N / 3] += (Real)h;
331 wq[N / 3] -= (Real)(2 * h);
333 std::printf(
" FD dE/dw_%d: analytic=%.4e fd=%.4e\n", N / 3, g[dw],
334 (Gwp.E - Gwm.E) / (2 * h));
343 std::printf(
" [vmesh] iter %2d E=%.4e maxVolErr=%.3e gnorm=%.3e nBad=%ld mu=%.2e\n", it, G.E,
344 G.maxErr, gnorm, G.nBad, muCur);
354 double rz0 = 0, rz = 0;
356 for (
int i = 0; i < nD; ++i) dq[i] = -g[i];
359 std::vector<Index> Hstart(nD + 1, 0), Hcol, ostart(nD + 1, 0), onbr;
360 std::vector<double> Hval, Hdiag(nD, 0.0);
361 for (
int i = 0; i < nD; ++i) {
362 Hstart[i] = (Index)Hcol.size();
363 ostart[i] = (Index)onbr.size();
364 for (
auto& [j, v] : row[i]) {
373 Hstart[nD] = (Index)Hcol.size();
374 ostart[nD] = (Index)onbr.size();
376 auto matvec = [&](
const std::vector<double>& v, std::vector<double>& out) {
377 for (
int i = 0; i < nD; ++i) {
379 for (Index k = Hstart[i]; k < Hstart[i + 1]; ++k) s += Hval[k] * v[Hcol[k]];
385 peclet::core::amr::Coloring col;
386 std::vector<Index> colIdxHost;
388 col = peclet::core::amr::greedyColoring(ostart, onbr, (Index)nD);
396 peclet::core::solver::GraphAMG amg;
398 peclet::core::solver::HostCsrOp Aop;
401 Aop.start.assign((std::size_t)nD + 1, 0);
402 for (
int i = 0; i < nD; ++i) {
403 for (Index k = Hstart[i]; k < Hstart[i + 1]; ++k)
405 Aop.nbr.push_back(Hcol[k]);
406 Aop.coef.push_back(Hval[k]);
408 Aop.start[(std::size_t)i + 1] = (Index)Aop.nbr.size();
410 peclet::core::solver::AmgParams ap;
411 ap.ndofPerNode = Weighted ? 1 : 3;
414 auto precond = [&](
const std::vector<double>& r, std::vector<double>& z) {
416 for (
int i = 0; i < nD; ++i) z[i] = std::fabs(Hdiag[i]) > 1e-30 ? r[i] / Hdiag[i] : r[i];
424 std::fill(z.begin(), z.end(), 0.0);
425 auto sweep = [&](
bool forward) {
426 for (
int ci = 0; ci < col.nColors; ++ci) {
427 const int cc = forward ? ci : col.nColors - 1 - ci;
428 for (Index t = col.hStart[cc]; t < col.hStart[cc + 1]; ++t) {
429 const Index i = colIdxHost[t];
431 for (Index k = Hstart[i]; k < Hstart[i + 1]; ++k)
432 if (Hcol[k] != i) s -= Hval[k] * z[Hcol[k]];
433 if (std::fabs(Hdiag[i]) > 1e-30) z[i] = s / Hdiag[i];
442 std::fill(dq.begin(), dq.end(), 0.0);
443 std::vector<double> rr(nD), z(nD), p(nD), Ap(nD);
444 for (
int i = 0; i < nD; ++i) rr[i] = -g[i];
448 for (
int i = 0; i < nD; ++i) rz += rr[i] * z[i];
450 for (
int k = 0; k < cgIters && rz > 1e-18 * rz0; ++k) {
453 for (
int i = 0; i < nD; ++i) pAp += p[i] * Ap[i];
455 const double a = rz / pAp;
456 for (
int i = 0; i < nD; ++i) {
462 for (
int i = 0; i < nD; ++i) rzn += rr[i] * z[i];
463 const double beta = rzn / rz;
464 for (
int i = 0; i < nD; ++i) p[i] = z[i] + beta * p[i];
471 for (
int i = 0; i < nD; ++i) gdq += g[i] * dq[i];
472 const double Ecur = G.E + barrierE(G, muCur);
473 std::vector<Real> q, qtry, xt, wt;
474 pack(pos, weight, q);
476 bool accepted =
false;
478 for (
int bt = 0; bt < 24; ++bt) {
480 for (
int i = 0; i < nD; ++i) qtry[i] = q[i] + (Real)(alpha * dq[i]);
481 unpack(qtry, xt, wt);
483 const double Etry = Gt.E + barrierE(Gt, muCur);
487 const bool cellsValid = (muCur > 0.0)
488 ? std::isfinite(Etry)
489 : (kHasSdf ? std::isfinite(Gt.E) : (Gt.nBad == 0));
490 if (cellsValid && Etry <= Ecur + 1e-4 * alpha * gdq) {
492 if constexpr (Weighted) weight = wt;
500 std::printf(
" prec=%s cg rz %.2e->%.2e alpha=%.4f\n",
502 if (!accepted)
break;
520 const Real L[3],
int N,
int sw,
const Sdf& sdf,
int maxIter, Real tol,
521 bool verbose =
false) {
522 using MemSpace = peclet::core::MemSpace;
524 const Real Larr[3] = {L[0], L[1], L[2]}, Lh[3] = {L[0] / 2, L[1] / 2, L[2] / 2};
525 Kokkos::View<Real*, MemSpace> dw;
526 Kokkos::View<long*, MemSpace> gd;
530 auto energyGrad = [&](
const std::vector<Real>& x, std::vector<double>& g,
double& E) {
531 Kokkos::View<Real*, MemSpace> dpos(
"if.pos", 3 * N);
532 Kokkos::deep_copy(dpos, Kokkos::View<const Real*, Kokkos::HostSpace>(x.data(), 3 * N));
533 auto res = buildTessellation<Real, false, Sdf>(dpos, dw, N, Larr, sw, N, gd, sdf,
true);
537 const int nF = (int)nbr.size();
538 g.assign(3 * N, 0.0);
540 for (
int c = 0; c < N; ++c) {
542 std::vector<std::array<Real, 4>> nb;
543 std::vector<int> ids;
544 for (
int f = off[c]; f < off[c] + cnt[c] && f < nF; ++f) {
545 const long j = (long)nbr[f];
546 if (j < 0 || j >= N)
continue;
548 for (
int d = 0; d < 3; ++d) {
549 Real rr = x[3 * j + d] - x[3 * c + d];
550 rr = rr > Lh[d] ? rr - L[d] : (rr < -Lh[d] ? rr + L[d] : rr);
553 nb.push_back({r[0] * r[0] + r[1] * r[1] + r[2] * r[2], r[0], r[1], r[2]});
554 ids.push_back((
int)j);
556 const int M = (int)ids.size();
557 std::vector<int> ord(M);
558 for (
int i = 0; i < M; ++i) ord[i] = i;
559 std::sort(ord.begin(), ord.end(), [&](
int a,
int b) { return nb[a][0] < nb[b][0]; });
560 std::vector<Real> rx(M), ry(M), rz(M);
561 std::vector<int> id2(M);
562 for (
int i = 0; i < M; ++i) {
563 rx[i] = nb[ord[i]][1];
564 ry[i] = nb[ord[i]][2];
565 rz[i] = nb[ord[i]][3];
566 id2[i] = ids[ord[i]];
569 buildConvexCell(cell, Larr, rx.data(), ry.data(), rz.data(), id2.data(), M);
570 if (cell.empty() || cell.overflow)
continue;
573 const int np = cell.np;
574 std::vector<double> Ag(np, 0.0), dA((
size_t)np * np * 3, 0.0);
575 for (
int t = 0; t < cell.nt; ++t) {
576 if (!cell.alive[t])
continue;
578 double cb[3], gr[3][3][3];
579 cell.geomVolumeAreaGrad(t, pl, cb, gr);
580 for (
int ii = 0; ii < 3; ++ii) {
581 Ag[pl[ii]] += cb[ii];
582 for (
int jj = 0; jj < 3; ++jj)
583 for (
int cc = 0; cc < 3; ++cc)
584 dA[((
size_t)pl[ii] * np + pl[jj]) * 3 + cc] += gr[ii][jj][cc];
589 std::vector<double> gn(3 * np, 0.0);
590 for (
int k = 0; k < np; ++k) {
591 const int j = cell.pnbr[k];
592 if (j < 0 || j >= N || type[j] == type[c])
continue;
593 E += 0.5 * sigma * Ag[k];
594 for (
int l = 0; l < np; ++l)
595 for (
int cc = 0; cc < 3; ++cc)
596 gn[3 * l + cc] += 0.5 * sigma * dA[((
size_t)k * np + l) * 3 + cc];
599 double gx[128], gy[128], gz[128];
600 for (
int l = 0; l < np; ++l) {
602 gy[l] = gn[3 * l + 1];
603 gz[l] = gn[3 * l + 2];
605 const double seed3[3] = {(double)x[3 * c], (
double)x[3 * c + 1], (double)x[3 * c + 2]};
606 double fSelf[3], fwSelf, fnx[128], fny[128], fnz[128], fwn[128];
607 chainToDofs<Voronoi>(cell, seed3, (
const double*)
nullptr, 0.0, (
const double*)
nullptr,
608 (
double)L[0], gx, gy, gz, fSelf, fwSelf, fnx, fny, fnz, fwn);
609 g[3 * c] += fSelf[0];
610 g[3 * c + 1] += fSelf[1];
611 g[3 * c + 2] += fSelf[2];
612 for (
int l = 0; l < np; ++l) {
613 const int j = cell.pnbr[l];
614 if (j < 0 || j >= N)
continue;
616 g[3 * j + 1] += fny[l];
617 g[3 * j + 2] += fnz[l];
623 std::vector<double> g;
625 energyGrad(pos, g, E);
628 const int d = 3 * (N / 2);
629 const double h = 1e-7;
630 std::vector<Real> xp = pos;
631 std::vector<double> gp, gm;
634 energyGrad(xp, gp, Ep);
635 xp[d] -= (Real)(2 * h);
636 energyGrad(xp, gm, Em);
637 std::printf(
" FD dE/dx_%d: analytic=%.4e fd=%.4e\n", d, g[d], (Ep - Em) / (2 * h));
639 for (
int it = 0; it < maxIter; ++it) {
641 for (
double v : g) gnorm = std::max(gnorm, std::fabs(v));
644 if (verbose) std::printf(
" [iface] iter %2d E=%.6e gnorm=%.3e\n", it, E, gnorm);
652 const double gdq = -std::inner_product(g.begin(), g.end(), g.begin(), 0.0);
653 const double spacing = std::cbrt((
double)L[0] * L[1] * L[2] / N);
654 double alpha = std::min(1.0, 0.02 * spacing / std::max(gnorm, 1e-30));
655 bool accepted =
false;
656 std::vector<Real> xtry(3 * N);
657 std::vector<double> gt;
659 for (
int bt = 0; bt < 30; ++bt) {
660 for (
int i = 0; i < 3 * N; ++i) xtry[i] = pos[i] - (Real)(alpha * g[i]);
661 energyGrad(xtry, gt, Et);
662 if (Et <= E + 1e-4 * alpha * gdq) {
671 if (!accepted)
break;
687 const Real L[3],
int N,
int sw,
const Sdf& sdf,
int maxNewton,
688 Real tol,
int cgIters = 300,
bool verbose =
false) {
689 using MemSpace = peclet::core::MemSpace;
690 using Exec = peclet::core::ExecSpace;
691 using DV = Kokkos::View<double*, MemSpace>;
692 const Real Larr[3] = {L[0], L[1], L[2]};
693 const double gamma = 1.0, boxVol = (double)L[0] * L[1] * L[2];
694 const int nD = 3 * N;
697 for (
int i = 0; i < N; ++i) sumVset += vsetIn[i];
698 DV vset(
"mo.vset", N);
700 auto h = Kokkos::create_mirror_view(vset);
701 for (
int i = 0; i < N; ++i) h(i) = vsetIn[i] * (boxVol / sumVset);
702 Kokkos::deep_copy(vset, h);
704 Kokkos::View<Real*, MemSpace> dpos(
"mo.pos", 3 * N), dw;
705 Kokkos::deep_copy(dpos, Kokkos::View<const Real*, Kokkos::HostSpace>(posHost.data(), 3 * N));
706 Kokkos::View<long*, MemSpace> gd;
708 DV g(
"mo.g", nD), dq(
"mo.dq", nD), diag(
"mo.diag", nD);
709 DV rr(
"mo.r", nD), z(
"mo.z", nD), pp(
"mo.p", nD), Ap(
"mo.Ap", nD), yy(
"mo.y", N);
713 double& E,
double& maxErr,
double& meanErr,
long& nBad) {
714 res = buildTessellation<Real, false, Sdf>(x, dw, N, Larr, sw, N, gd, sdf,
true);
715 auto vol = res.view.cellVolume;
717 double e = 0, mx = 0, mn = 0;
719 Kokkos::parallel_reduce(
720 "mo.E", Kokkos::RangePolicy<Exec>(0, N),
721 KOKKOS_LAMBDA(
int i,
double& le,
double& lmx,
double& lmn,
long& lnb) {
722 const double d = vol(i) - vs(i);
724 lmx = Kokkos::max(lmx, Kokkos::fabs(d));
725 lmn += Kokkos::fabs(d);
726 if (vol(i) <= 0.0) ++lnb;
728 e, Kokkos::Max<double>(mx), mn, nb);
736 double E, maxErr, meanErr;
738 evaluate(dpos, res, E, maxErr, meanErr, nBad);
741 for (
int it = 0; it < maxNewton; ++it) {
742 auto off = res.view.cellFacetOffset;
743 auto cnt = res.view.cellFacetCount;
744 auto nbr = res.view.facetNeighbor;
745 auto dvr = res.view.facetConnect;
746 auto vol = res.view.cellVolume;
750 Kokkos::deep_copy(g, 0.0);
751 Kokkos::deep_copy(diag, 0.0);
752 Kokkos::parallel_for(
753 "mo.grad", Kokkos::RangePolicy<Exec>(0, N), KOKKOS_LAMBDA(
int c) {
754 const double Rc = 2.0 * gamma * (vol(c) - vs(c));
755 double sx = 0, sy = 0, sz = 0;
756 for (
int f = off(c); f < off(c) + cnt(c); ++f) {
757 const int j = (int)nbr(f);
758 if (j < 0 || j >= N)
continue;
759 const double dx = dvr(3 * f), dy = dvr(3 * f + 1), dz = dvr(3 * f + 2);
760 Kokkos::atomic_add(&g(3 * j), Rc * dx);
761 Kokkos::atomic_add(&g(3 * j + 1), Rc * dy);
762 Kokkos::atomic_add(&g(3 * j + 2), Rc * dz);
763 Kokkos::atomic_add(&diag(3 * j), 2.0 * gamma * dx * dx);
764 Kokkos::atomic_add(&diag(3 * j + 1), 2.0 * gamma * dy * dy);
765 Kokkos::atomic_add(&diag(3 * j + 2), 2.0 * gamma * dz * dz);
770 Kokkos::atomic_add(&g(3 * c), Rc * sx);
771 Kokkos::atomic_add(&g(3 * c + 1), Rc * sy);
772 Kokkos::atomic_add(&g(3 * c + 2), Rc * sz);
773 Kokkos::atomic_add(&diag(3 * c), 2.0 * gamma * sx * sx);
774 Kokkos::atomic_add(&diag(3 * c + 1), 2.0 * gamma * sy * sy);
775 Kokkos::atomic_add(&diag(3 * c + 2), 2.0 * gamma * sz * sz);
778 Kokkos::parallel_reduce(
779 "mo.gnorm", Kokkos::RangePolicy<Exec>(0, nD),
780 KOKKOS_LAMBDA(
int i,
double& m) { m = Kokkos::max(m, Kokkos::fabs(g(i))); },
781 Kokkos::Max<double>(gnorm));
788 std::printf(
" [dvmesh] iter %2d E=%.4e maxVolErr=%.3e gnorm=%.3e nBad=%ld\n", it, E, maxErr,
796 auto Hmul = [&](
const DV& v, DV& out) {
798 Kokkos::parallel_for(
799 "mo.Jv", Kokkos::RangePolicy<Exec>(0, N), KOKKOS_LAMBDA(
int c) {
801 for (
int f = off(c); f < off(c) + cnt(c); ++f) {
802 const int j = (int)nbr(f);
803 if (j < 0 || j >= N)
continue;
804 for (
int d = 0; d < 3; ++d) yc += dvr(3 * f + d) * (v(3 * j + d) - v(3 * c + d));
808 Kokkos::deep_copy(out, 0.0);
809 Kokkos::parallel_for(
810 "mo.JTy", Kokkos::RangePolicy<Exec>(0, N), KOKKOS_LAMBDA(
int c) {
811 const double s = 2.0 * gamma * y(c);
812 for (
int f = off(c); f < off(c) + cnt(c); ++f) {
813 const int j = (int)nbr(f);
814 if (j < 0 || j >= N)
continue;
815 for (
int d = 0; d < 3; ++d) {
816 Kokkos::atomic_add(&out(3 * j + d), s * dvr(3 * f + d));
817 Kokkos::atomic_add(&out(3 * c + d), -s * dvr(3 * f + d));
822 auto dot = [&](
const DV& a,
const DV& b) {
824 Kokkos::parallel_reduce(
825 "mo.dot", Kokkos::RangePolicy<Exec>(0, nD),
826 KOKKOS_LAMBDA(
int i,
double& l) { l += a(i) * b(i); }, s);
831 Kokkos::deep_copy(dq, 0.0);
833 auto G = g, D = diag;
834 Kokkos::parallel_for(
835 "mo.r0", Kokkos::RangePolicy<Exec>(0, nD), KOKKOS_LAMBDA(
int i) {
837 z(i) = Kokkos::fabs(D(i)) > 1e-30 ? rr(i) / D(i) : rr(i);
841 double rz = dot(rr, z), rz0 = rz;
842 for (
int k = 0; k < cgIters && rz > 1e-18 * rz0; ++k) {
844 const double pAp = dot(pp, Ap);
846 const double a = rz / pAp;
849 Kokkos::parallel_for(
850 "mo.cgupd", Kokkos::RangePolicy<Exec>(0, nD), KOKKOS_LAMBDA(
int i) {
853 z(i) = Kokkos::fabs(D(i)) > 1e-30 ? rr(i) / D(i) : rr(i);
856 const double rzn = dot(rr, z);
857 const double beta = rzn / rz;
858 Kokkos::parallel_for(
859 "mo.pupd", Kokkos::RangePolicy<Exec>(0, nD),
860 KOKKOS_LAMBDA(
int i) { pp(i) = z(i) + beta * pp(i); });
863 const double gdq = dot(g, dq);
867 bool accepted =
false;
868 Kokkos::View<Real*, MemSpace> xtry(
"mo.xtry", 3 * N);
869 for (
int bt = 0; bt < 24; ++bt) {
870 const double al = alpha;
871 auto X = dpos, DQ = dq, XT = xtry;
872 Kokkos::parallel_for(
873 "mo.trial", Kokkos::RangePolicy<Exec>(0, nD),
874 KOKKOS_LAMBDA(
int i) { XT(i) = X(i) + (Real)(al * DQ(i)); });
878 evaluate(xtry, resT, Et, mxT, mnT, nbT);
879 if (nbT == 0 && Et <= E + 1e-4 * alpha * gdq) {
880 Kokkos::deep_copy(dpos, xtry);
891 if (!accepted)
break;
894 auto h = Kokkos::create_mirror_view(dpos);
895 Kokkos::deep_copy(h, dpos);
896 for (
int i = 0; i < 3 * N; ++i) posHost[i] = h(i);