18#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
19#pragma GCC diagnostic push
20#pragma GCC diagnostic ignored "-Wunused-function"
23#ifndef GSLIB_RELEASE_VERSION
24#define GSLIB_RELEASE_VERSION 10007
26#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
27#pragma GCC diagnostic pop
32#if GSLIB_RELEASE_VERSION >= 10009
33#define CODE_INTERNAL 0
35#define CODE_NOT_FOUND 2
39struct findptsElementPoint_t
41 double x[
DIM], r[
DIM], oldr[
DIM], dist2, dist2p, tr;
45struct findptsElementGEdge_t
47 double *x[
DIM], *dxdn[2];
50struct findptsElementGPT_t
65static MFEM_HOST_DEVICE
inline void lin_solve_2(
double x[2],
const double A[4],
68 const double idet = 1/(A[0]*A[3] - A[1]*A[2]);
69 x[0] = idet*(A[3]*y[0] - A[1]*y[1]);
70 x[1] = idet*(A[0]*y[1] - A[2]*y[0]);
81#define CONVERGED_FLAG (1u<<4)
82#define FLAG_MASK 0x1fu
85static MFEM_HOST_DEVICE
inline int num_constrained(
const int flags)
87 const int y = flags | flags >> 1;
88 return (y&1u) + (y>>2 & 1u);
92static MFEM_HOST_DEVICE
inline int plus_1_mod_2(
const int x)
97static MFEM_HOST_DEVICE
inline int which_bit(
const int x)
100 return (y-(y>>2)) | ((x-1)&4u);
104static MFEM_HOST_DEVICE
inline int edge_index(
const int x)
106 return which_bit(x)-1;
110static MFEM_HOST_DEVICE
inline int point_index(
const int x)
112 return ((x>>1)&1u) | ((x>>2)&2u);
117static MFEM_HOST_DEVICE
inline findptsElementGEdge_t
118get_edge(
const double *elx[2],
const double *wtend,
int ei,
120 int &side_init,
int j,
int pN)
122 findptsElementGEdge_t edge;
123 const int jidx = ei >= 2 ? j : ei*(pN-1);
124 const int kidx = ei >= 2 ? (ei-2)*(pN-1) : j;
128 const double *wt1 = wtend + (ei%2==0 ? 0 : 1)* pN * 3 + pN;
130 for (
int d = 0; d < 2; ++d)
132 edge.x[d] = workspace + d * pN;
133 edge.dxdn[d] = workspace + (2 + d) * pN;
136 if (
static_cast<unsigned>(side_init) != (1u << ei))
138#define ELX(d, j, k) elx[d][j + k * pN]
139 for (
int d = 0; d < 2; ++d)
142 edge.x[d][j] = ELX(d, jidx, kidx);
146 for (
int k = 0; k < pN; ++k)
150 sums_k += wt1[k] * ELX(d, j, k);
154 sums_k += wt1[k] * ELX(d, k, j);
157 edge.dxdn[d][j] = sums_k;
168static MFEM_HOST_DEVICE
inline findptsElementGPT_t get_pt(
const double *elx[2],
172 findptsElementGPT_t pt;
174#define ELX(d, j, k) elx[d][j + k * pN]
176 int r_g_wt_offset = pi % 2 == 0 ? 0 : 1;
177 int s_g_wt_offset = pi < 2 ? 0 : 1;
178 int jidx = pi % 2 == 0 ? 0 : pN-1;
179 int kidx = pi < 2 ? 0 : pN-1;
181 pt.x[0] = ELX(0, jidx, kidx);
182 pt.x[1] = ELX(1, jidx, kidx);
188 for (
int j = 0; j < pN; ++j)
191 pt.jac[0] += wtend[3 * r_g_wt_offset * pN + pN + j] * ELX(0, j, kidx);
194 pt.jac[2] += wtend[3 * r_g_wt_offset * pN + pN + j] * ELX(1, j, kidx);
197 pt.jac[1] += wtend[3 * s_g_wt_offset * pN + pN + j] * ELX(0, kidx, j);
200 pt.jac[3] += wtend[3 * s_g_wt_offset * pN + pN + j] * ELX(1, kidx, j);
207 for (
int j = 0; j < pN; ++j)
210 pt.hes[0] += wtend[3 * r_g_wt_offset * pN + 2*pN + j] * ELX(0, j, kidx);
213 pt.hes[2] += wtend[3 * r_g_wt_offset * pN + 2*pN + j] * ELX(1, j, kidx);
216 pt.hes[1] += wtend[3 * s_g_wt_offset * pN + 2*pN + j] * ELX(0, kidx, j);
219 pt.hes[3] += wtend[3 * s_g_wt_offset * pN + 2*pN + j] * ELX(1, kidx, j);
229static MFEM_HOST_DEVICE
bool reject_prior_step_q(findptsElementPoint_t *res,
230 const double resid[2],
231 const findptsElementPoint_t *
p,
234 const double dist2 = l2norm2<2>(resid);
235 const double decr =
p->dist2 - dist2;
236 const double pred =
p->dist2p;
237 for (
int d = 0; d < 2; ++d)
240 res->oldr[d] =
p->r[d];
243 if (decr >= 0.01 * pred)
245 if (decr >= 0.9 * pred)
263 double v0 = fabs(
p->r[0] -
p->oldr[0]);
264 double v1 = fabs(
p->r[1] -
p->oldr[1]);
265 res->tr = (v0 > v1 ? v0 : v1)/4;
266 res->dist2 =
p->dist2;
267 for (
int d = 0; d < 2; ++d)
269 res->r[d] =
p->oldr[d];
271 res->flags =
p->flags >> 5;
272 res->dist2p = -HUGE_VAL;
273 if (pred < dist2 * tol)
275 res->flags |= CONVERGED_FLAG;
283static MFEM_HOST_DEVICE
void newton_area(findptsElementPoint_t *
const res,
285 const double resid[2],
286 const findptsElementPoint_t *
const p,
289 const double tr =
p->tr;
290 double bnd[4] = {-1, 1, -1, 1};
294 r0[0] =
p->r[0], r0[1] =
p->r[1];
297 for (d = 0; d < 2; ++d)
301 bnd[2 * d] = r0[d] -
tr, mask ^= 1u << (2 * d);
305 bnd[2 * d + 1] = r0[d] +
tr, mask ^= 2u << (2 * d);
310 lin_solve_2(dr, jac, resid);
313 for (d = 0; d < 2; ++d)
315 double nr = r0[d] + dr[d];
316 if ((nr - bnd[2 * d]) * (bnd[2 * d + 1] - nr) >= 0)
322 double f = (bnd[2 * d] - r0[d]) / dr[d];
325 fac =
f, flags = 1u << (2 * d);
330 double f = (bnd[2 * d + 1] - r0[d]) / dr[d];
333 fac =
f, flags = 2u << (2 * d);
340 goto newton_area_fin;
343 for (d = 0; d < 2; ++d)
350 const int ei = edge_index(flags);
351 const int dn = ei>>1, de = plus_1_mod_2(dn);
354 double ress[2], y, JtJ, drc;
355 ress[0] = resid[0] - (jac[0] * dr[0] + jac[1] * dr[1]);
356 ress[1] = resid[1] - (jac[2] * dr[0] + jac[3] * dr[1]);
358 y = jac[de] * ress[0] + jac[2+de] * ress[1];
360 JtJ = jac[de] * jac[de] + jac[2+de] * jac[2+de];
363 const double rz = r0[de] + dr[de], lb = bnd[2*de], ub = bnd[2*de+1];
364 const double nr = r0[de]+(dr[de]+drc);
365 if ((nr-lb) * (ub-nr) < 0)
369 double f = (lb-rz)/drc;
373 new_flags = 1u<<(2*de);
378 double f = (ub-rz)/drc;
382 new_flags = 2u<<(2*de);
388 dr[de] += facc * drc;
390 goto newton_area_relax;
396 const int old_flags = flags;
397 double ress[2], y[2];
399 ress[0] = resid[0] - (jac[0] * dr[0] + jac[1] * dr[1]);
400 ress[1] = resid[1] - (jac[2] * dr[0] + jac[3] * dr[1]);
402 y[0] = jac[0] * ress[0] + jac[2] * ress[1];
403 y[1] = jac[1] * ress[0] + jac[3] * ress[1];
404 for (
int dd = 0; dd < 2; ++dd)
406 int f = flags >> (2 * dd) & 3u;
409 dr[dd] = bnd[2 * dd + (
f - 1)] - r0[dd];
410 if (dr[dd] * y[dd] < 0)
412 flags &= ~(3u << (2 * dd));
416 if (flags == old_flags)
418 goto newton_area_fin;
420 switch (num_constrained(flags))
423 goto newton_area_edge;
429 if (fabs(dr[0]) + fabs(dr[1]) < tol)
431 flags |= CONVERGED_FLAG;
434 const double res0 = resid[0] - (jac[0] * dr[0] + jac[1] * dr[1]);
435 const double res1 = resid[1] - (jac[2] * dr[0] + jac[3] * dr[1]);
436 res->dist2p = resid[0] * resid[0] + resid[1] * resid[1] -
437 (res0 * res0 + res1 * res1);
439 for (
int dd = 0; dd < 2; ++dd)
441 int f = flags >> (2 * dd) & 3u;
442 res->r[dd] =
f == 0 ? r0[dd] + dr[dd] : (
f == 1 ? -1 : 1);
444 res->flags = flags | ((
p->flags & FLAG_MASK) << 5);
448static MFEM_HOST_DEVICE
inline void newton_edge(findptsElementPoint_t *
const
452 const double resid[2],
456 const findptsElementPoint_t *
const p,
459 const double tr =
p->tr;
461 const double A = jac[de] * jac[de] + jac[2 + de] * jac[2 + de] - rhes;
463 const double y = jac[de] * resid[0] + jac[2 + de] * resid[1];
465 const double oldr =
p->r[de];
466 double dr, nr, tdr, tnr;
468 int new_flags = 0, tnew_flags = 0;
470#define EVAL(dr) (dr * A - 2 * y) * dr
475 dr = y / A, nr = oldr + dr;
476 if (fabs(dr) < tr && fabs(nr) < 1)
479 goto newton_edge_fin;
483 if ((nr = oldr - tr) > -1)
489 nr = -1, dr = -1 - oldr, new_flags = flags | 1u << (2 * de);
493 if ((tnr = oldr + tr) < 1)
499 tnr = 1, tdr = 1 - oldr, tnew_flags = flags | 2u << (2 * de);
505 nr = tnr, dr = tdr, v = tv, new_flags = tnew_flags;
512 new_flags |= CONVERGED_FLAG;
517 res->flags = flags | new_flags | ((
p->flags & FLAG_MASK) << 5);
522static MFEM_HOST_DEVICE
void seed_j(
const double *elx[2],
533 for (
int k = 0; k < pN; ++k)
536 const int jk = j + k * pN;
538 for (
int d = 0; d < 2; ++d)
540 dx[d] = x[d] - elx[d][jk];
542 const double dist2_jkl = l2norm2(dx);
543 if (dist2[j] > dist2_jkl)
545 dist2[j] = dist2_jkl;
554static MFEM_HOST_DEVICE
double tensor_ig2_j(
double *g_partials,
565 for (
int k = 0; k < pN; ++k)
567 uJs +=
u[j + k * pN] * Js[k];
568 uDs +=
u[j + k * pN] * Ds[k];
571 g_partials[0] = uJs * Dr[j];
572 g_partials[1] = uDs * Jr[j];
576template<
int T_D1D = 0>
577static void FindPointsLocal2DKernel(
const int npt,
580 const int point_pos_ordering,
581 const double *xElemCoord,
584 const double *boxinfo,
586 const double *hashMin,
587 const double *hashFac,
588 unsigned int *hashOffset,
589 unsigned int *
const code_base,
590 unsigned int *
const el_base,
591 double *
const r_base,
592 double *
const dist2_base,
594 const double *lagcoeff,
597 const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
598 const int D1D = T_D1D ? T_D1D : pN;
599 const int p_NE = D1D*D1D;
600 const int p_NEL = nel*p_NE;
601 MFEM_VERIFY(MD1 <= DofQuadLimits::MAX_D1D,
602 "Increase Max allowable polynomial order.");
603 MFEM_VERIFY(D1D != 0,
"Polynomial order not specified.");
604 const int nThreads = D1D*
DIM;
609 constexpr int size1 = 10*MD1 + 6;
610 constexpr int size2 = MD1*4;
611 constexpr int size3 = MD1*MD1*
DIM;
613 MFEM_SHARED
double r_workspace[size1];
614 MFEM_SHARED findptsElementPoint_t el_pts[2];
616 MFEM_SHARED
double constraint_workspace[size2];
617 MFEM_SHARED
int edge_init;
619 MFEM_SHARED
double elem_coords[MD1 <= 6 ? size3 : 1];
621 double *r_workspace_ptr;
622 findptsElementPoint_t *fpt, *tmp;
623 MFEM_FOREACH_THREAD(j,x,nThreads)
625 r_workspace_ptr = r_workspace;
631 int id_x = point_pos_ordering == 0 ? i : i*
DIM;
632 int id_y = point_pos_ordering == 0 ? i+npt : i*
DIM+1;
633 double x_i[2] = {x[id_x], x[id_y]};
635 unsigned int *code_i = code_base + i;
636 unsigned int *el_i = el_base + i;
637 double *r_i = r_base +
DIM * i;
638 double *dist2_i = dist2_base + i;
641 *code_i = CODE_NOT_FOUND;
646 for (
int d = 0; d <
DIM; ++d)
648 hash.bnd[d].min = hashMin[d];
649 hash.fac[d] = hashFac[d];
651 hash.hash_n = hash_n;
652 hash.offset = hashOffset;
653 const int hi = hash_index(&hash, x_i);
654 const unsigned int *elp = hash.offset+hash.offset[hi],
655 *
const ele = hash.offset+hash.offset[hi+1];
657 for (; elp != ele; ++elp)
660 const unsigned int el = *elp;
664 int n_box_ents = 3*
DIM + DIM2;
666 for (
int idx = 0; idx <
DIM; ++idx)
668 box.c0[idx] = boxinfo[n_box_ents*el + idx];
669 box.x[idx].min = boxinfo[n_box_ents*el +
DIM + idx];
670 box.x[idx].max = boxinfo[n_box_ents*el + 2*
DIM + idx];
673 for (
int idx = 0; idx < DIM2; ++idx)
675 box.A[idx] = boxinfo[n_box_ents*el + 3*
DIM + idx];
678 if (bbox_test(&box, x_i) < 0) {
continue; }
685 MFEM_FOREACH_THREAD(j,x,nThreads)
687 const int qp = j % D1D;
688 const int d = j / D1D;
689 for (
int k = 0; k < D1D; ++k)
691 const int jk = qp + k * D1D;
692 elem_coords[jk + d*p_NE] =
693 xElemCoord[jk + el*p_NE + d*p_NEL];
699 const double *elx[
DIM];
700 for (
int d = 0; d <
DIM; d++)
702 elx[d] = MD1<= 6 ? &elem_coords[d*p_NE] :
703 xElemCoord + d*p_NEL + el * p_NE;
708 MFEM_FOREACH_THREAD(j,x,1)
710 fpt->dist2 = HUGE_VAL;
715 MFEM_FOREACH_THREAD(j,x,
DIM)
723 double *dist2_temp = r_workspace_ptr;
725 for (
int d = 0; d <
DIM; ++d)
727 r_temp[d] = dist2_temp + (1 + d) * D1D;
730 MFEM_FOREACH_THREAD(j,x,D1D)
732 seed_j(elx, x_i, gll1D, dist2_temp, r_temp, j, D1D);
736 MFEM_FOREACH_THREAD(j,x,1)
738 fpt->dist2 = HUGE_VAL;
739 for (
int jj = 0; jj < D1D; ++jj)
741 if (dist2_temp[jj] < fpt->dist2)
743 fpt->dist2 = dist2_temp[jj];
744 for (
int d = 0; d <
DIM; ++d)
746 fpt->r[d] = r_temp[d][jj];
754 MFEM_FOREACH_THREAD(j,x,1)
756 tmp->dist2 = HUGE_VAL;
761 MFEM_FOREACH_THREAD(j,x,
DIM)
763 tmp->x[j] = fpt->x[j];
764 tmp->r[j] = fpt->r[j];
768 for (
int step = 0; step < 50; step++)
770 switch (num_constrained(tmp->flags & FLAG_MASK))
774 double *wtr = r_workspace_ptr;
775 double *resid = wtr + 4 * D1D;
776 double *jac = resid + 2;
777 double *resid_temp = jac + 4;
778 double *jac_temp = resid_temp + 2 * D1D;
780 MFEM_FOREACH_THREAD(j,x,nThreads)
782 const int qp = j % D1D;
783 const int d = j / D1D;
784 lag_eval_first_der(wtr + 2*d*D1D, tmp->r[d], qp,
785 gll1D, lagcoeff, D1D);
789 MFEM_FOREACH_THREAD(j,x,nThreads)
791 const int qp = j % D1D;
792 const int d = j / D1D;
793 double *idx = jac_temp+2*d+4*qp;
794 resid_temp[d+qp*2] = tensor_ig2_j(idx, wtr,
803 MFEM_FOREACH_THREAD(l,x,2)
805 resid[l] = tmp->x[l];
806 for (
int j = 0; j < D1D; ++j)
808 resid[l] -= resid_temp[l + j * 2];
811 MFEM_FOREACH_THREAD(l,x,4)
814 for (
int j = 0; j < D1D; ++j)
816 jac[l] += jac_temp[l + j * 4];
821 MFEM_FOREACH_THREAD(l,x,1)
823 if (!reject_prior_step_q(fpt, resid, tmp, tol))
825 newton_area(fpt, jac, resid, tmp, tol);
833 const int ei = edge_index(tmp->flags & FLAG_MASK);
834 const int dn = ei>>1, de = plus_1_mod_2(dn);
836 double *wt = r_workspace_ptr;
837 double *resid = wt + 3 * D1D;
838 double *jac = resid + 2;
839 double *hess = jac + 2 * 2;
840 findptsElementGEdge_t edge;
842 MFEM_FOREACH_THREAD(j,x,D1D)
844 edge = get_edge(elx, wtend, ei,
845 constraint_workspace,
852 MFEM_FOREACH_THREAD(j,x,D1D)
854 if (j == 0) { edge_init = 1u << ei; }
855 lag_eval_second_der(wt, tmp->r[de], j, gll1D,
860 MFEM_FOREACH_THREAD(d,x,
DIM)
862 resid[d] = tmp->x[d];
866 for (
int k = 0; k < D1D; ++k)
868 resid[d] -= wt[k]*edge.x[d][k];
869 jac[2*d] += wt[k]*edge.dxdn[d][k];
870 jac[2*d+1] += wt[k+D1D]*edge.x[d][k];
871 hess[d] += wt[k+2*D1D]*edge.x[d][k];
879 MFEM_FOREACH_THREAD(j,x,1)
883 double temp1 = jac[1],
890 hess[2] = resid[0]*hess[0] + resid[1]*hess[1];
894 MFEM_FOREACH_THREAD(l,x,1)
897 if (!reject_prior_step_q(fpt, resid, tmp, tol))
902 double steep = resid[0] * jac[ dn]
903 + resid[1] * jac[2+dn];
905 if (steep * tmp->r[dn] < 0)
907 newton_area(fpt, jac, resid, tmp, tol);
911 newton_edge(fpt, jac, hess[2], resid, de,
912 dn, tmp->flags & FLAG_MASK,
922 MFEM_FOREACH_THREAD(j,x,1)
926 const int pi = point_index(tmp->flags & FLAG_MASK);
927 const findptsElementGPT_t gpt =
928 get_pt(elx, wtend, pi, D1D);
930 const double *
const pt_x = gpt.x;
931 const double *
const jac = gpt.jac;
932 const double *
const hes = gpt.hes;
935 for (
int d = 0; d <
DIM; ++d)
937 resid[d] = fpt->x[d] - pt_x[d];
939 steep[0] = jac[0]*resid[0] + jac[2]*resid[1];
940 steep[1] = jac[1]*resid[0] + jac[3]*resid[1];
942 sr[0] = steep[0]*tmp->r[0];
943 sr[1] = steep[1]*tmp->r[1];
945 if (!reject_prior_step_q(fpt, resid, tmp, tol))
951 newton_area(fpt, jac, resid, tmp, tol);
957 const double rh = resid[0]*hes[de]+
959 newton_edge(fpt, jac, rh, resid, de, dn,
970 const double rh = resid[0]*hes[de]+
972 newton_edge(fpt, jac, rh, resid, de, dn,
980 fpt->r[0] = tmp->r[0];
981 fpt->r[1] = tmp->r[1];
983 fpt->flags = tmp->flags | CONVERGED_FLAG;
991 if (fpt->flags & CONVERGED_FLAG)
996 MFEM_FOREACH_THREAD(j,x,1)
1004 bool converged_internal = (fpt->flags&FLAG_MASK)==CONVERGED_FLAG;
1005 if (*code_i == CODE_NOT_FOUND || converged_internal ||
1006 fpt->dist2 < *dist2_i)
1008 MFEM_FOREACH_THREAD(j,x,1)
1011 *code_i = converged_internal ? CODE_INTERNAL :
1013 *dist2_i = fpt->dist2;
1015 MFEM_FOREACH_THREAD(j,x,
DIM)
1020 if (converged_internal)
1031 int point_pos_ordering,
1040 auto pp = point_pos.
Read();
1047 auto pcode = code.
Write();
1048 auto pelem = elem.
Write();
1049 auto pref = ref.
Write();
1050 auto pdist = dist.
Write();
1058 point_pos_ordering, pgslm,
1061 pcode, pelem, pref, pdist,
1066 point_pos_ordering, pgslm,
1069 pcode, pelem, pref, pdist,
1074 point_pos_ordering, pgslm,
1077 pcode, pelem, pref, pdist,
1082 point_pos_ordering, pgslm,
1085 pcode, pelem, pref, pdist,
1090 point_pos_ordering, pgslm,
1093 pcode, pelem, pref, pdist,
1102#undef CODE_NOT_FOUND
1105 int point_pos_ordering,
1108 Vector &dist,
int npt) {};
T * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), on_dev).
T * Write(bool on_dev=true)
Shortcut for mfem::Write(a.GetMemory(), a.Size(), on_dev).
struct mfem::FindPointsGSLIB::DevStruct DEV
void FindPointsLocal2(const Vector &point_pos, int point_pos_ordering, Array< unsigned int > &gsl_code_dev_l, Array< unsigned int > &gsl_elem_dev_l, Vector &gsl_ref_l, Vector &gsl_dist_l, int npt)
FindPoints locally on device for 2D.
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
MFEM_HOST_DEVICE T tr(const tensor< T, n, n > &A)
Returns the trace of a square matrix.
MFEM_HOST_DEVICE double bbox_test(const obbox_t< SDIM > *const b, const double(&x)[SDIM])
MFEM_HOST_DEVICE void lag_eval_first_der(double *p0, double x, int i, const double *z, const double *lCoeff, int pN)
MFEM_HOST_DEVICE double l2norm2(const double(&x)[SDIM])
MFEM_HOST_DEVICE int hash_index(const findptsLocalHashData_t< SDIM > *const p, const double(&x)[SDIM])
MFEM_HOST_DEVICE void lag_eval_second_der(double *p0, double x, int i, const double *z, const double *lCoeff, int pN)
real_t u(const Vector &xvec)
gslib::obbox_t< DIM > obbox_t
gslib::findptsLocalHashData_t< DIM > findptsLocalHashData_t
void forall_2D(int N, int X, int Y, lambda &&body)
std::function< real_t(const Vector &)> f(real_t mass_coeff)
real_t p(const Vector &x, real_t t)
Array< unsigned int > lh_offset