66 const std::vector<Real>& vsetIn,
const Real L[3],
int N,
int sw,
67 const Sdf& sdf,
int maxNewton, Real tol,
69 bool verbose =
false, Real muBarrier = 0,
70 Real muDecay = (Real)
kBarrierDecay,
bool freeEnergy =
false) {
71 using peclet::core::Index;
72 using MemSpace = peclet::core::MemSpace;
73 const Real Larr[3] = {L[0], L[1], L[2]};
74 constexpr bool kHasSdf = !std::is_same_v<Sdf, peclet::voro::NoSdf>;
75 const int nD = Weighted ? 4 * N : 3 * N;
76 auto wdof = [N](
int i) {
return 3 * N + i; };
78 Kokkos::View<long*, MemSpace> gd;
79 const double boxVol = (double)L[0] * L[1] * L[2];
81 for (
int i = 0; i < N; ++i)
83 std::vector<double> vset(N);
84 for (
int i = 0; i < N; ++i)
85 vset[i] = vsetIn[i] * (boxVol / sumVset);
92 const double barEps = 0.1;
93 auto barCell = [&](
double V,
double vref,
double mu) {
99 const double t = V / vref;
101 r.e = -mu * std::log(t);
105 const double dt = t - barEps, ie = 1.0 / barEps;
106 r.e = mu * (-std::log(barEps) - dt * ie + 0.5 * dt * dt * ie * ie);
107 r.d1 = mu * (-ie + dt * ie * ie) / vref;
108 r.d2 = mu * ie * ie / (vref * vref);
112 const double feMin = 0.1;
113 auto freeCell = [&](
double V,
double vref) {
117 const double Vmin = feMin * vref, iv = 1.0 / Vmin;
119 r.e = -vref * std::log(V);
121 r.d2 = vref / (V * V);
123 const double dV = V - Vmin;
124 r.e = -vref * (std::log(Vmin) + dV * iv - 0.5 * dV * dV * iv * iv);
125 r.d1 = -vref * (iv - dV * iv * iv);
126 r.d2 = vref * iv * iv;
132 std::vector<double> vol, dvr, area, conn;
133 std::vector<int> off, cnt;
134 std::vector<peclet::voro::gid_t> nbr;
135 double E = 0, maxErr = 0, meanErr = 0;
138 auto build = [&](
const std::vector<Real>& x,
const std::vector<Real>& w, Geo& G) {
139 Kokkos::View<Real*, MemSpace> dpos(
"mo.pos", 3 * N), dw;
140 Kokkos::deep_copy(dpos, Kokkos::View<const Real*, Kokkos::HostSpace>(x.data(), 3 * N));
141 if constexpr (Weighted) {
142 dw = Kokkos::View<Real*, MemSpace>(
"mo.w", N);
143 Kokkos::deep_copy(dw, Kokkos::View<const Real*, Kokkos::HostSpace>(w.data(), N));
145 auto res = buildTessellation<Real, Weighted, Sdf>(dpos, dw, N, Larr, sw, N, gd, sdf,
true);
153 G.E = G.maxErr = G.meanErr = 0;
155 for (
int i = 0; i < N; ++i) {
156 const double e = G.vol[i] / vset[i] - 1.0;
157 G.E += freeEnergy ? freeCell(G.vol[i], vset[i]).e : e * e;
158 G.maxErr = std::max(G.maxErr, std::fabs(e));
159 G.meanErr += std::fabs(e);
168 auto stencil = [&](
const Geo& G,
int c, std::vector<std::pair<int, double>>& st) {
170 const int nF = (int)G.nbr.size();
171 double sx = 0, sy = 0, sz = 0, sw2 = 0;
172 for (
int f = G.off[c]; f < G.off[c] + G.cnt[c] && f < nF; ++f) {
173 const long j = (long)G.nbr[f];
176 const double dx = G.dvr[3 * f], dy = G.dvr[3 * f + 1], dz = G.dvr[3 * f + 2];
177 st.emplace_back(3 * (
int)j, dx);
178 st.emplace_back(3 * (
int)j + 1, dy);
179 st.emplace_back(3 * (
int)j + 2, dz);
183 if constexpr (Weighted) {
185 std::sqrt(G.area[3 * f] * G.area[3 * f] + G.area[3 * f + 1] * G.area[3 * f + 1] +
186 G.area[3 * f + 2] * G.area[3 * f + 2]);
188 std::sqrt(G.conn[3 * f] * G.conn[3 * f] + G.conn[3 * f + 1] * G.conn[3 * f + 1] +
189 G.conn[3 * f + 2] * G.conn[3 * f + 2]);
190 const double wgt = (d > 0) ? A / (2 * d) : 0;
191 st.emplace_back(wdof((
int)j), -wgt);
202 if constexpr (kHasSdf) {
204 double gw[3] = {0, 0, 0};
205 for (
int f = G.off[c]; f < G.off[c] + G.cnt[c] && f < nF; ++f)
207 gw[0] += G.area[3 * f];
208 gw[1] += G.area[3 * f + 1];
209 gw[2] += G.area[3 * f + 2];
214 double fw[3] = {0.0, 0.0, 0.0};
215 sdfWallChain<Real>(sdf, pos[3 * c], pos[3 * c + 1], pos[3 * c + 2], gw, fw);
221 st.emplace_back(3 * c, sx);
222 st.emplace_back(3 * c + 1, sy);
223 st.emplace_back(3 * c + 2, sz);
224 if constexpr (Weighted)
225 st.emplace_back(wdof(c), sw2);
229 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)
245 G.E = G.maxErr = G.meanErr = 0;
247 for (
int i = 0; i < N; ++i) {
248 const double e = G.vol[i] / vset[i] - 1.0;
249 G.E += freeEnergy ? freeCell(G.vol[i], vset[i]).e : e * e;
250 G.maxErr = std::max(G.maxErr, std::fabs(e));
251 G.meanErr += std::fabs(e);
257 auto pack = [&](
const std::vector<Real>& x,
const std::vector<Real>& w, std::vector<Real>& q) {
258 q.assign(x.begin(), x.end());
259 if constexpr (Weighted)
260 q.insert(q.end(), w.begin(), w.end());
262 auto unpack = [&](
const std::vector<Real>& q, std::vector<Real>& x, std::vector<Real>& w) {
263 x.assign(q.begin(), q.begin() + 3 * N);
264 if constexpr (Weighted)
265 w.assign(q.begin() + 3 * N, q.end());
268 auto barrierE = [&](
const Geo& G,
double mu) ->
double {
269 if (freeEnergy || mu <= 0.0)
272 for (
int i = 0; i < N; ++i)
273 b += barCell(G.vol[i], vset[i], mu).e;
278 std::vector<double> g(nD), dq(nD);
279 std::vector<std::pair<int, double>> st;
281 for (
int it = 0; it < maxNewton; ++it) {
284 const double muCur = freeEnergy ? 0.0 : (double)muBarrier * std::pow((
double)muDecay, it);
286 std::fill(g.begin(), g.end(), 0.0);
287 std::vector<std::unordered_map<int, double>> row(nD);
288 for (
int c = 0; c < N; ++c) {
296 const auto fc = freeCell(G.vol[c], vset[c]);
300 Rc = 2.0 * (G.vol[c] / vset[c] - 1.0) / vset[c];
301 Hw = 2.0 / (vset[c] * vset[c]);
303 const auto bc = barCell(G.vol[c], vset[c], muCur);
309 for (
auto& [a, va] : st) {
313 for (
auto& [b, vb] : st)
314 ra[b] += Hw * va * vb;
319 for (
int i = 0; i < nD; ++i)
320 gnorm = std::max(gnorm, std::fabs(g[i]));
322 if (verbose && it == 0) {
323 const int d = 3 * (N / 2);
324 const double h = 1e-7;
325 std::vector<Real> xp = pos, wp = weight;
329 xp[d] -= (Real)(2 * h);
331 std::printf(
" FD dE/dx_%d: analytic=%.4e fd=%.4e\n", d, g[d],
332 ((Gp.E + barrierE(Gp, muCur)) - (Gm.E + barrierE(Gm, muCur))) / (2 * h));
333 if constexpr (Weighted) {
334 const int dw = wdof(N / 3);
335 std::vector<Real> wq = weight;
336 wq[N / 3] += (Real)h;
339 wq[N / 3] -= (Real)(2 * h);
341 std::printf(
" FD dE/dw_%d: analytic=%.4e fd=%.4e\n", N / 3, g[dw],
342 (Gwp.E - Gwm.E) / (2 * h));
351 std::printf(
" [vmesh] iter %2d E=%.4e maxVolErr=%.3e gnorm=%.3e nBad=%ld mu=%.2e\n", it,
352 G.E, G.maxErr, gnorm, G.nBad, muCur);
362 double rz0 = 0, rz = 0;
364 for (
int i = 0; i < nD; ++i)
368 std::vector<Index> Hstart(nD + 1, 0), Hcol, ostart(nD + 1, 0), onbr;
369 std::vector<double> Hval, Hdiag(nD, 0.0);
370 for (
int i = 0; i < nD; ++i) {
371 Hstart[i] = (Index)Hcol.size();
372 ostart[i] = (Index)onbr.size();
373 for (
auto& [j, v] : row[i]) {
382 Hstart[nD] = (Index)Hcol.size();
383 ostart[nD] = (Index)onbr.size();
385 auto matvec = [&](
const std::vector<double>& v, std::vector<double>& out) {
386 for (
int i = 0; i < nD; ++i) {
388 for (Index k = Hstart[i]; k < Hstart[i + 1]; ++k)
389 s += Hval[k] * v[Hcol[k]];
395 peclet::core::solver::Coloring col;
396 std::vector<Index> colIdxHost;
398 col = peclet::core::solver::greedyColoring(ostart, onbr, (Index)nD);
407 peclet::core::solver::GraphAMG amg;
409 peclet::core::solver::HostCsrOp Aop;
412 Aop.start.assign((std::size_t)nD + 1, 0);
413 for (
int i = 0; i < nD; ++i) {
414 for (Index k = Hstart[i]; k < Hstart[i + 1]; ++k)
416 Aop.nbr.push_back(Hcol[k]);
417 Aop.coef.push_back(Hval[k]);
419 Aop.start[(std::size_t)i + 1] = (Index)Aop.nbr.size();
421 peclet::core::solver::AmgParams ap;
422 ap.ndofPerNode = Weighted ? 1 : 3;
425 auto precond = [&](
const std::vector<double>& r, std::vector<double>& z) {
427 for (
int i = 0; i < nD; ++i)
428 z[i] = std::fabs(Hdiag[i]) > 1e-30 ? r[i] / Hdiag[i] : r[i];
436 std::fill(z.begin(), z.end(), 0.0);
437 auto sweep = [&](
bool forward) {
438 for (
int ci = 0; ci < col.nColors; ++ci) {
439 const int cc = forward ? ci : col.nColors - 1 - ci;
440 for (Index t = col.hStart[cc]; t < col.hStart[cc + 1]; ++t) {
441 const Index i = colIdxHost[t];
443 for (Index k = Hstart[i]; k < Hstart[i + 1]; ++k)
445 s -= Hval[k] * z[Hcol[k]];
446 if (std::fabs(Hdiag[i]) > 1e-30)
456 std::fill(dq.begin(), dq.end(), 0.0);
457 std::vector<double> rr(nD), z(nD), p(nD), Ap(nD);
458 for (
int i = 0; i < nD; ++i)
463 for (
int i = 0; i < nD; ++i)
466 for (
int k = 0; k < cgIters && rz > 1e-18 * rz0; ++k) {
469 for (
int i = 0; i < nD; ++i)
473 const double a = rz / pAp;
474 for (
int i = 0; i < nD; ++i) {
480 for (
int i = 0; i < nD; ++i)
482 const double beta = rzn / rz;
483 for (
int i = 0; i < nD; ++i)
484 p[i] = z[i] + beta * p[i];
491 for (
int i = 0; i < nD; ++i)
493 const double Ecur = G.E + barrierE(G, muCur);
494 std::vector<Real> q, qtry, xt, wt;
495 pack(pos, weight, q);
497 bool accepted =
false;
499 for (
int bt = 0; bt < 24; ++bt) {
501 for (
int i = 0; i < nD; ++i)
502 qtry[i] = q[i] + (Real)(alpha * dq[i]);
503 unpack(qtry, xt, wt);
505 const double Etry = Gt.E + barrierE(Gt, muCur);
509 const bool cellsValid =
510 (muCur > 0.0) ? std::isfinite(Etry) : (kHasSdf ? std::isfinite(Gt.E) : (Gt.nBad == 0));
511 if (cellsValid && Etry <= Ecur + 1e-4 * alpha * gdq) {
513 if constexpr (Weighted)
522 std::printf(
" prec=%s cg rz %.2e->%.2e alpha=%.4f\n",
668 double sigma,
const Real L[3],
int N,
int sw,
const Sdf& sdf,
669 int maxIter, Real tol,
bool verbose =
false) {
670 using MemSpace = peclet::core::MemSpace;
671 const Real Larr[3] = {L[0], L[1], L[2]};
672 Kokkos::View<Real*, MemSpace> dw;
673 Kokkos::View<long*, MemSpace> gd;
680 nTypes = std::max(nTypes, t + 1);
681 Kokkos::View<int*, MemSpace> dtype(
"if.type", N);
682 Kokkos::deep_copy(dtype, Kokkos::View<const int*, Kokkos::HostSpace>(type.data(), N));
683 Kokkos::View<Real*, MemSpace> dten(
"if.tension", (
size_t)nTypes * nTypes);
685 auto h = Kokkos::create_mirror_view(dten);
686 for (
int a = 0; a < nTypes; ++a)
687 for (
int b = 0; b < nTypes; ++b)
688 h(a * nTypes + b) = (a == b) ? Real(0) : (Real)sigma;
689 Kokkos::deep_copy(dten, h);
691 Kokkos::View<Real*, MemSpace> dpos(
"if.pos", 3 * N), dforce(
"if.force", 3 * N);
692 auto energyGrad = [&](
const std::vector<Real>& x, std::vector<double>& g,
double& E) {
693 Kokkos::deep_copy(dpos, Kokkos::View<const Real*, Kokkos::HostSpace>(x.data(), 3 * N));
694 auto res = buildTessellation<Real, false, Sdf>(dpos, dw, N, Larr, sw, N, gd, sdf,
true, -1, {},
695 {}, {}, {}, {}, {}, 0,
nullptr, {},
697 Kokkos::deep_copy(dforce, Real(0));
698 E = (double)energy::interfaceEnergyForce<Real, Voronoi, Sdf>(res.view, dtype, dten, nTypes,
699 dpos, dw, L[0], dforce, {}, sdf);
701 g.assign(gh.begin(), gh.end());
705 std::vector<double> g;
707 energyGrad(pos, g, E);
710 const int d = 3 * (N / 2);
711 const double h = 1e-7;
712 std::vector<Real> xp = pos;
713 std::vector<double> gp, gm;
716 energyGrad(xp, gp, Ep);
717 xp[d] -= (Real)(2 * h);
718 energyGrad(xp, gm, Em);
719 std::printf(
" FD dE/dx_%d: analytic=%.4e fd=%.4e\n", d, g[d], (Ep - Em) / (2 * h));
721 for (
int it = 0; it < maxIter; ++it) {
724 gnorm = std::max(gnorm, std::fabs(v));
728 std::printf(
" [iface] iter %2d E=%.6e gnorm=%.3e\n", it, E, gnorm);
736 const double gdq = -std::inner_product(g.begin(), g.end(), g.begin(), 0.0);
737 const double spacing = std::cbrt((
double)L[0] * L[1] * L[2] / N);
738 double alpha = std::min(1.0, 0.02 * spacing / std::max(gnorm, 1e-30));
739 bool accepted =
false;
740 std::vector<Real> xtry(3 * N);
741 std::vector<double> gt;
743 for (
int bt = 0; bt < 30; ++bt) {
744 for (
int i = 0; i < 3 * N; ++i)
745 xtry[i] = pos[i] - (Real)(alpha * g[i]);
746 energyGrad(xtry, gt, Et);
747 if (Et <= E + 1e-4 * alpha * gdq) {
759 R.energyRatio = E / std::max(E0, 1e-30);
773 const Real L[3],
int N,
int sw,
const Sdf& sdf,
int maxNewton,
774 Real tol,
int cgIters = kOptimizerCgIters,
bool verbose =
false) {
775 using MemSpace = peclet::core::MemSpace;
776 using Exec = peclet::core::ExecSpace;
777 using DV = Kokkos::View<double*, MemSpace>;
778 const Real Larr[3] = {L[0], L[1], L[2]};
779 const double gamma = 1.0, boxVol = (double)L[0] * L[1] * L[2];
780 const int nD = 3 * N;
783 for (
int i = 0; i < N; ++i)
784 sumVset += vsetIn[i];
785 DV vset(
"mo.vset", N);
787 auto h = Kokkos::create_mirror_view(vset);
788 for (
int i = 0; i < N; ++i)
789 h(i) = vsetIn[i] * (boxVol / sumVset);
790 Kokkos::deep_copy(vset, h);
792 Kokkos::View<Real*, MemSpace> dpos(
"mo.pos", 3 * N), dw;
793 Kokkos::deep_copy(dpos, Kokkos::View<const Real*, Kokkos::HostSpace>(posHost.data(), 3 * N));
794 Kokkos::View<long*, MemSpace> gd;
796 constexpr bool kHasSdfD = !std::is_same_v<Sdf, peclet::voro::NoSdf>;
797 DV g(
"mo.g", nD), dq(
"mo.dq", nD), diag(
"mo.diag", nD);
798 DV rr(
"mo.r", nD), z(
"mo.z", nD), pp(
"mo.p", nD), Ap(
"mo.Ap", nD), yy(
"mo.y", N);
799 DV wallG(
"mo.wallG", nD);
804 auto vol = res.
view.cellVolume;
806 double e = 0, mx = 0, mn = 0;
808 Kokkos::parallel_reduce(
809 "mo.E", Kokkos::RangePolicy<Exec>(0, N),
810 KOKKOS_LAMBDA(
int i,
double& le,
double& lmx,
double& lmn,
long& lnb) {
811 const double d = vol(i) - vs(i);
813 lmx = Kokkos::max(lmx, Kokkos::fabs(d));
814 lmn += Kokkos::fabs(d);
818 e, Kokkos::Max<double>(mx), mn, nb);
826 double& E,
double& maxErr,
double& meanErr,
long& nBad) {
827 res = buildTessellation<Real, false, Sdf>(x, dw, N, Larr, sw, N, gd, sdf,
true);
828 score(res, E, maxErr, meanErr, nBad);
832 double E, maxErr, meanErr;
834 evaluate(dpos, res, E, maxErr, meanErr, nBad);
835 if constexpr (kHasSdfD) {
840 auto vol0 = res.
view.cellVolume;
841 Kokkos::parallel_reduce(
842 "mo.totvol", Kokkos::RangePolicy<Exec>(0, N),
843 KOKKOS_LAMBDA(
int i,
double& l) { l += vol0(i); }, totVol);
844 const double sc = (totVol > 0) ? totVol / boxVol : 1.0;
846 Kokkos::parallel_for(
847 "mo.vscale", Kokkos::RangePolicy<Exec>(0, N), KOKKOS_LAMBDA(
int i) { vsc(i) *= sc; });
848 score(res, E, maxErr, meanErr, nBad);
852 for (
int it = 0; it < maxNewton; ++it) {
853 auto off = res.
view.cellFacetOffset;
854 auto cnt = res.
view.cellFacetCount;
855 auto nbr = res.
view.facetNeighbor;
856 auto dvr = res.
view.facetConnect;
857 auto vol = res.
view.cellVolume;
864 if constexpr (kHasSdfD) {
865 auto area = res.
view.facetArea;
869 Kokkos::parallel_for(
870 "mo.wall", Kokkos::RangePolicy<Exec>(0, N), KOKKOS_LAMBDA(
int c) {
871 double gw0 = 0, gw1 = 0, gw2 = 0;
872 for (
int f = off(c); f < off(c) + cnt(c); ++f)
875 gw1 += area(3 * f + 1);
876 gw2 += area(3 * f + 2);
879 const double gwd[3] = {gw0, gw1, gw2};
880 double fw[3] = {0.0, 0.0, 0.0};
881 sdfWallChain<Real>(sdfD, X(3 * c), X(3 * c + 1), X(3 * c + 2), gwd, fw);
882 const double wx = fw[0], wy = fw[1], wz = fw[2];
890 Kokkos::deep_copy(g, 0.0);
891 Kokkos::deep_copy(diag, 0.0);
893 Kokkos::parallel_for(
894 "mo.grad", Kokkos::RangePolicy<Exec>(0, N), KOKKOS_LAMBDA(
int c) {
895 const double Rc = 2.0 * gamma * (vol(c) - vs(c));
896 double sx = wGc(3 * c), sy = wGc(3 * c + 1), sz = wGc(3 * c + 2);
897 for (
int f = off(c); f < off(c) + cnt(c); ++f) {
898 const int j = (int)nbr(f);
901 const double dx = dvr(3 * f), dy = dvr(3 * f + 1), dz = dvr(3 * f + 2);
902 Kokkos::atomic_add(&g(3 * j), Rc * dx);
903 Kokkos::atomic_add(&g(3 * j + 1), Rc * dy);
904 Kokkos::atomic_add(&g(3 * j + 2), Rc * dz);
905 Kokkos::atomic_add(&diag(3 * j), 2.0 * gamma * dx * dx);
906 Kokkos::atomic_add(&diag(3 * j + 1), 2.0 * gamma * dy * dy);
907 Kokkos::atomic_add(&diag(3 * j + 2), 2.0 * gamma * dz * dz);
912 Kokkos::atomic_add(&g(3 * c), Rc * sx);
913 Kokkos::atomic_add(&g(3 * c + 1), Rc * sy);
914 Kokkos::atomic_add(&g(3 * c + 2), Rc * sz);
915 Kokkos::atomic_add(&diag(3 * c), 2.0 * gamma * sx * sx);
916 Kokkos::atomic_add(&diag(3 * c + 1), 2.0 * gamma * sy * sy);
917 Kokkos::atomic_add(&diag(3 * c + 2), 2.0 * gamma * sz * sz);
920 Kokkos::parallel_reduce(
921 "mo.gnorm", Kokkos::RangePolicy<Exec>(0, nD),
922 KOKKOS_LAMBDA(
int i,
double& m) { m = Kokkos::max(m, Kokkos::fabs(g(i))); },
923 Kokkos::Max<double>(gnorm));
930 std::printf(
" [dvmesh] iter %2d E=%.4e maxVolErr=%.3e gnorm=%.3e nBad=%ld\n", it, E, maxErr,
938 auto Hmul = [&](
const DV& v, DV& out) {
941 Kokkos::parallel_for(
942 "mo.Jv", Kokkos::RangePolicy<Exec>(0, N), KOKKOS_LAMBDA(
int c) {
944 for (
int f = off(c); f < off(c) + cnt(c); ++f) {
945 const int j = (int)nbr(f);
948 for (
int d = 0; d < 3; ++d)
949 yc += dvr(3 * f + d) * (v(3 * j + d) - v(3 * c + d));
951 for (
int d = 0; d < 3; ++d)
952 yc += wGv(3 * c + d) * v(3 * c + d);
955 Kokkos::deep_copy(out, 0.0);
956 Kokkos::parallel_for(
957 "mo.JTy", Kokkos::RangePolicy<Exec>(0, N), KOKKOS_LAMBDA(
int c) {
958 const double s = 2.0 * gamma * y(c);
959 for (
int f = off(c); f < off(c) + cnt(c); ++f) {
960 const int j = (int)nbr(f);
963 for (
int d = 0; d < 3; ++d) {
964 Kokkos::atomic_add(&out(3 * j + d), s * dvr(3 * f + d));
965 Kokkos::atomic_add(&out(3 * c + d), -s * dvr(3 * f + d));
968 for (
int d = 0; d < 3; ++d)
969 Kokkos::atomic_add(&out(3 * c + d), s * wGv(3 * c + d));
972 auto dot = [&](
const DV& a,
const DV& b) {
974 Kokkos::parallel_reduce(
975 "mo.dot", Kokkos::RangePolicy<Exec>(0, nD),
976 KOKKOS_LAMBDA(
int i,
double& l) { l += a(i) * b(i); }, s);
981 Kokkos::deep_copy(dq, 0.0);
983 auto G = g, D = diag;
984 Kokkos::parallel_for(
985 "mo.r0", Kokkos::RangePolicy<Exec>(0, nD), KOKKOS_LAMBDA(
int i) {
987 z(i) = Kokkos::fabs(D(i)) > 1e-30 ? rr(i) / D(i) : rr(i);
991 double rz = dot(rr, z), rz0 = rz;
992 for (
int k = 0; k < cgIters && rz > 1e-18 * rz0; ++k) {
994 const double pAp = dot(pp, Ap);
997 const double a = rz / pAp;
1000 Kokkos::parallel_for(
1001 "mo.cgupd", Kokkos::RangePolicy<Exec>(0, nD), KOKKOS_LAMBDA(
int i) {
1004 z(i) = Kokkos::fabs(D(i)) > 1e-30 ? rr(i) / D(i) : rr(i);
1007 const double rzn = dot(rr, z);
1008 const double beta = rzn / rz;
1009 Kokkos::parallel_for(
1010 "mo.pupd", Kokkos::RangePolicy<Exec>(0, nD),
1011 KOKKOS_LAMBDA(
int i) { pp(i) = z(i) + beta * pp(i); });
1014 const double gdq = dot(g, dq);
1019 Kokkos::View<Real*, MemSpace> xtry(
"mo.xtry", 3 * N);
1020 auto lineSearch = [&](
const DV& dir,
double gdir) {
1022 for (
int bt = 0; bt < 24; ++bt) {
1023 const double al = alpha;
1024 auto X = dpos, DQ = dir, XT = xtry;
1025 Kokkos::parallel_for(
1026 "mo.trial", Kokkos::RangePolicy<Exec>(0, nD),
1027 KOKKOS_LAMBDA(
int i) { XT(i) = X(i) + (Real)(al * DQ(i)); });
1029 double Et, mxT, mnT;
1031 evaluate(xtry, resT, Et, mxT, mnT, nbT);
1032 const bool feasible = kHasSdfD ? std::isfinite(Et) : (nbT == 0);
1033 if (feasible && Et <= E + 1e-4 * alpha * gdir) {
1034 Kokkos::deep_copy(dpos, xtry);
1046 bool accepted = lineSearch(dq, gdq);
1049 auto G = g, DQ = dq;
1050 Kokkos::parallel_for(
1051 "mo.sd", Kokkos::RangePolicy<Exec>(0, nD), KOKKOS_LAMBDA(
int i) { DQ(i) = -G(i); });
1052 accepted = lineSearch(dq, -dot(g, g));
1058 auto h = Kokkos::create_mirror_view(dpos);
1059 Kokkos::deep_copy(h, dpos);
1060 for (
int i = 0; i < 3 * N; ++i)