MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
findpts_local_3.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2026, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#include "../gslib.hpp"
15
16#ifdef MFEM_USE_GSLIB
17
18#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
19#pragma GCC diagnostic push
20#pragma GCC diagnostic ignored "-Wunused-function"
21#endif
22#include "gslib.h"
23#ifndef GSLIB_RELEASE_VERSION //gslib v1.0.7
24#define GSLIB_RELEASE_VERSION 10007
25#endif
26#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
27#pragma GCC diagnostic pop
28#endif
29namespace mfem
30{
31#if GSLIB_RELEASE_VERSION >= 10009
32#define CODE_INTERNAL 0
33#define CODE_BORDER 1
34#define CODE_NOT_FOUND 2
35#define DIM 3
36#define DIM2 DIM*DIM
37#define pMax 10
38
39struct findptsPt
40{
41 double x[DIM], r[DIM], oldr[DIM], dist2, dist2p, tr;
42 int flags;
43};
44
45struct findptsElemFace
46{
47 double *x[DIM], *dxdn[DIM];
48};
49
50struct findptsElemEdge
51{
52 double *x[DIM], *dxdn1[DIM], *dxdn2[DIM], *d2xdn1[DIM], *d2xdn2[DIM];
53};
54
55struct findptsElemPt
56{
57 double x[DIM], jac[DIM * DIM], hes[18];
58};
59
60using dbl_range_t = gslib::dbl_range_t;
61using obbox_t = gslib::obbox_t<DIM>;
62using findptsLocalHashData_t = gslib::findptsLocalHashData_t<DIM>;
65using gslib::l2norm2;
69
70// Solve Ax=y. A is row-major.
71static MFEM_HOST_DEVICE inline void lin_solve_3(double x[3], const double A[9],
72 const double y[3])
73{
74 const double a = A[4]*A[8]-A[5]*A[7], b = A[5]*A[6]-A[3]*A[8],
75 c = A[3]*A[7]-A[4]*A[6],
76 idet = 1 / (A[0]*a+A[1]*b+A[2]*c);
77 const double inv0 = a, inv1 = A[2]*A[7]-A[1]*A[8],
78 inv2 = A[1]*A[5]-A[2]*A[4], inv3 = b,
79 inv4 = A[0]*A[8]-A[2]*A[6], inv5 = A[2]*A[3]-A[0]*A[5],
80 inv6 = c, inv7 = A[1]*A[6]-A[0]*A[7],
81 inv8 = A[0]*A[4]-A[1]*A[3];
82 x[0] = idet*(inv0*y[0]+inv1*y[1]+inv2*y[2]);
83 x[1] = idet*(inv3*y[0]+inv4*y[1]+inv5*y[2]);
84 x[2] = idet*(inv6*y[0]+inv7*y[1]+inv8*y[2]);
85}
86
87/* the bit structure of flags is CTTSSRR
88 the C bit --- 1<<6 --- is set when the point is converged
89 RR is 0 = 00b if r is unconstrained,
90 1 = 01b if r is constrained at -1
91 2 = 10b if r is constrained at +1
92 SS, TT are similarly for s and t constraints
93 TT is ignored, but treated as set when 3==2
94*/
95#define CONVERGED_FLAG (1u << 6)
96#define FLAG_MASK 0x7fu // set all 7 bits to 1
97
98// Determine number of constraints based on the CTTSSRR bits.
99static MFEM_HOST_DEVICE inline int num_constrained(const int flags)
100{
101 const int y = flags | flags >> 1;
102 return (y & 1u)+(y >> 2 & 1u)+(((3 == 2) | y >> 4) & 1u);
103}
104
105// Helper functions. Assumes x = 0, 1, or 2.
106static MFEM_HOST_DEVICE inline int plus_1_mod_3(const int x)
107{
108 return ((x | x >> 1)+1) & 3u;
109}
110static MFEM_HOST_DEVICE inline int plus_2_mod_3(const int x)
111{
112 const int y = (x-1) & 3u;
113 return y ^ (y >> 1);
114}
115
116// Assumes x = 1 << i, with i < 6, returns i+1.
117static MFEM_HOST_DEVICE inline int which_bit(const int x)
118{
119 const int y = x & 7u;
120 return (y-(y >> 2)) | ((x-1) & 4u) | (x >> 4);
121}
122
123// Get face index based on the TTSSRR bits.
124static MFEM_HOST_DEVICE inline int face_index(const int x)
125{
126 return which_bit(x)-1;
127}
128
129// Get edge index based on the TTSSRR bits.
130static MFEM_HOST_DEVICE inline int edge_index(const int x)
131{
132 const int y = ~((x >> 1) | x);
133 const int RTSR = ((x >> 1) & 1u) | ((x >> 2) & 2u) |
134 ((x >> 3) & 4u) | ((x << 2) & 8u);
135 const int re = RTSR >> 1;
136 const int se = 4u | RTSR >> 2;
137 const int te = 8u | (RTSR & 3u);
138 return ((0u-(y & 1u)) & re) | ((0u-((y >> 2) & 1u)) & se) |
139 ((0u-((y >> 4) & 1u)) & te);
140}
141
142// Get point index based on the TTSSRR bits.
143static MFEM_HOST_DEVICE inline int point_index(const int x)
144{
145 return ((x >> 1) & 1u) | ((x >> 2) & 2u) | ((x >> 3) & 4u);
146}
147
148// Gets mesh nodal coordinates and normal derivative in reference-space at the
149// given face.
150static MFEM_HOST_DEVICE inline findptsElemFace
151get_face(const double *elx[3], const double *wtend, int fi, double *workspace,
152 int &side_init, int jidx, int pN) // jidx < 3*pN
153{
154 const int dn = fi >> 1, d1 = plus_1_mod_3(dn), d2 = plus_2_mod_3(dn);
155 const int side_n = fi & 1;
156 const int p_Nfr = pN*pN;
157 findptsElemFace face;
158 const int jj = jidx % pN;
159 const int dd = jidx / pN;
160 for (int d = 0; d < 3; ++d)
161 {
162 face.x[d] = workspace+d*p_Nfr;
163 face.dxdn[d] = workspace+(3+d)*p_Nfr;
164 }
165
166 if (static_cast<unsigned>(side_init) != (1u << fi))
167 {
168 const int e_stride[3] = {1, pN, pN*pN};
169#define ELX(d, j, k, l) elx[d][j*e_stride[d1]+k*e_stride[d2]+l*e_stride[dn]]
170 for (int k = 0; k < pN; ++k)
171 {
172 // copy first/last entries in normal direction
173 face.x[dd][jj+k*pN] = ELX(dd, jj, k, side_n*(pN-1));
174
175 // tensor product between elx and derivative in normal direction
176 double sum_l = 0;
177 for (int l = 0; l < pN; ++l)
178 {
179 sum_l += wtend[pN+l]*ELX(dd, jj, k, l);
180 }
181 face.dxdn[dd][jj+k*pN] = sum_l;
182 }
183#undef ELX
184 }
185 return face;
186}
187
188// Gets mesh nodal coordinates and normal derivatives in reference-space at the
189// given edge.
190static MFEM_HOST_DEVICE inline findptsElemEdge
191get_edge(const double *elx[3], const double *wtend, int ei, double *workspace,
192 int &side_init, int jidx, int pN)
193{
194 findptsElemEdge edge;
195 const int de = ei >> 2, dn1 = plus_1_mod_3(de), dn2 = plus_2_mod_3(de);
196 const int side_n1 = ei & 1, side_n2 = (ei & 2) >> 1;
197
198 const int in1 = side_n1*(pN-1), in2 = side_n2*(pN-1);
199 const double *wt1 = wtend+side_n1*pN*3;
200 const double *wt2 = wtend+side_n2*pN*3;
201 const int jj = jidx % pN;
202 const int dd = jidx / pN;
203 for (int d = 0; d < 3; ++d)
204 {
205 edge.x[d] = workspace+d*pN;
206 edge.dxdn1[d] = workspace+(3+d)*pN;
207 edge.dxdn2[d] = workspace+(6+d)*pN;
208 edge.d2xdn1[d] = workspace+(9+d)*pN;
209 edge.d2xdn2[d] = workspace+(12+d)*pN;
210 }
211
212 if (jidx >= 3*pN) { return edge; }
213
214 if (static_cast<unsigned>(side_init) != (64u << ei))
215 {
216 const int e_stride[3] = {1, pN, pN*pN};
217#define ELX(d, j, k, l) elx[d][j*e_stride[de]+k*e_stride[dn1]+l*e_stride[dn2]]
218 // copy first/last entries in normal directions
219 edge.x[dd][jj] = ELX(dd, jj, in1, in2);
220 // tensor product between elx (w/ first/last entries in second direction)
221 // and the derivatives in the first normal direction
222 double sums_k[2] = {0, 0};
223 for (int k = 0; k < pN; ++k)
224 {
225 sums_k[0] += wt1[pN+k]*ELX(dd, jj, k, in2);
226 sums_k[1] += wt1[2*pN+k]*ELX(dd, jj, k, in2);
227 }
228 edge.dxdn1[dd][jj] = sums_k[0];
229 edge.d2xdn1[dd][jj] = sums_k[1];
230 // tensor product between elx (w/ first/last entries in first direction)
231 // and the derivatives in the second normal direction
232 sums_k[0] = 0, sums_k[1] = 0;
233 for (int k = 0; k < pN; ++k)
234 {
235 sums_k[0] += wt2[pN+k]*ELX(dd, jj, in1, k);
236 sums_k[1] += wt2[2*pN+k]*ELX(dd, jj, in1, k);
237 }
238 edge.dxdn2[dd][jj] = sums_k[0];
239 edge.d2xdn2[dd][jj] = sums_k[1];
240#undef ELX
241 }
242 return edge;
243}
244
245// Gets nodal coordinate and derivatives at the given vertex of the element.
246static MFEM_HOST_DEVICE inline findptsElemPt get_pt(const double *elx[3],
247 const double *wtend,
248 int pi, int pN)
249{
250 const int side_n1 = pi & 1, side_n2 = (pi >> 1) & 1, side_n3 = (pi >> 2) & 1;
251 const int in1 = side_n1*(pN-1), in2 = side_n2*(pN-1),
252 in3 = side_n3*(pN-1);
253 const int hes_stride = (3+1)*3 / 2;
254 findptsElemPt pt;
255
256#define ELX(d, j, k, l) elx[d][j+k*pN+l*pN*pN]
257 for (int d = 0; d < 3; ++d)
258 {
259 pt.x[d] = ELX(d, side_n1*(pN-1), side_n2*(pN-1),
260 side_n3*(pN-1));
261
262 const double *wt1 = wtend+pN*(1+3*side_n1);
263 const double *wt2 = wtend+pN*(1+3*side_n2);
264 const double *wt3 = wtend+pN*(1+3*side_n3);
265
266 for (int i = 0; i < 3; ++i)
267 {
268 pt.jac[3*d+i] = 0;
269 }
270 for (int i = 0; i < hes_stride; ++i)
271 {
272 pt.hes[hes_stride*d+i] = 0;
273 }
274
275 for (int j = 0; j < pN; ++j)
276 {
277 pt.jac[3*d+0] += wt1[j]*ELX(d, j, in2, in3);
278 pt.hes[hes_stride*d] += wt1[pN+j]*ELX(d, j, in2, in3);
279 }
280
281 const int hes_off = hes_stride*d+hes_stride / 2;
282 for (int k = 0; k < pN; ++k)
283 {
284 pt.jac[3*d+1] += wt2[k]*ELX(d, in1, k, in3);
285 pt.hes[hes_off] += wt2[pN+k]*ELX(d, in1, k, in3);
286 }
287
288 for (int l = 0; l < pN; ++l)
289 {
290 pt.jac[3*d+2] += wt3[l]*ELX(d, in1, in2, l);
291 pt.hes[hes_stride*d+5] += wt3[pN+l]*ELX(d, in1, in2, l);
292 }
293
294 for (int l = 0; l < pN; ++l)
295 {
296 double sum_k = 0, sum_j = 0;
297 for (int k = 0; k < pN; ++k)
298 {
299 sum_k += wt2[k]*ELX(d, in1, k, l);
300 }
301 for (int j = 0; j < pN; ++j)
302 {
303 sum_j += wt1[j]*ELX(d, j, in2, l);
304 }
305 pt.hes[hes_stride*d+2] += wt3[l]*sum_j;
306 pt.hes[hes_stride*d+4] += wt3[l]*sum_k;
307 }
308 for (int k = 0; k < pN; ++k)
309 {
310 double sum_j = 0;
311 for (int j = 0; j < pN; ++j)
312 {
313 sum_j += wt1[j]*ELX(d, j, k, in3);
314 }
315 pt.hes[hes_stride*d+1] += wt2[k]*sum_j;
316 }
317#undef ELX
318 }
319 return pt;
320}
321
322/* Check reduction in objective against prediction, and adjust trust region
323 radius (p->tr) accordingly. May reject the prior step, returning 1; otherwise
324 returns 0 sets res->dist2, res->index, res->x, res->oldr in any event,
325 leaving res->r, res->dr, res->flags to be set when returning 0 */
326static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsPt *res,
327 const double resid[3],
328 const findptsPt *p,
329 const double tol)
330{
331 const double dist2 = l2norm2<3>(resid);
332 const double decr = p->dist2-dist2;
333 const double pred = p->dist2p;
334 for (int d = 0; d < 3; ++d)
335 {
336 res->x[d] = p->x[d];
337 res->oldr[d] = p->r[d];
338 }
339 res->dist2 = dist2;
340 if (decr >= 0.01*pred)
341 {
342 if (decr >= 0.9*pred)
343 {
344 // very good iteration
345 res->tr = p->tr*2;
346 }
347 else
348 {
349 // good iteration
350 res->tr = p->tr;
351 }
352 return false;
353 }
354 else
355 {
356 /* reject step; note: the point will pass through this routine
357 again, and we set things up here so it gets classed as a
358 "very good iteration" --- this doubles the trust radius,
359 which is why we divide by 4 below */
360 double v0 = fabs(p->r[0]-p->oldr[0]);
361 double v1 = fabs(p->r[1]-p->oldr[1]);
362 double v2 = fabs(p->r[2]-p->oldr[2]);
363 res->tr = (v1 > v2 ? (v0 > v1 ? v0 : v1) : (v0 > v2 ? v0 : v2)) / 4;
364 res->dist2 = p->dist2;
365 for (int d = 0; d < 3; ++d)
366 {
367 res->r[d] = p->oldr[d];
368 }
369 res->flags = p->flags >> 7;
370 res->dist2p = -HUGE_VAL;
371 if (pred < dist2*tol)
372 {
373 res->flags |= CONVERGED_FLAG;
374 }
375 return true;
376 }
377}
378
379/* minimize 0.5||x* - x(r)||^2_2 using gradient-descent, with
380 |dr| <= tr and |r0+dr|<=1*/
381static MFEM_HOST_DEVICE void newton_vol(findptsPt *const res,
382 const double jac[9],
383 const double resid[3],
384 const findptsPt *const p,
385 const double tol)
386{
387 const double tr = p->tr;
388 double bnd[6] = {-1, 1, -1, 1, -1, 1};
389 double r0[3];
390 double dr[3], fac;
391 int d, mask, flags;
392 r0[0] = p->r[0], r0[1] = p->r[1], r0[2] = p->r[2];
393
394 mask = 0x3fu;
395 for (d = 0; d < 3; ++d)
396 {
397 if (r0[d]-tr > -1)
398 {
399 bnd[2*d] = r0[d]-tr, mask ^= 1u << (2*d);
400 }
401 if (r0[d]+tr < 1)
402 {
403 bnd[2*d+1] = r0[d]+tr, mask ^= 2u << (2*d);
404 }
405 }
406
407 lin_solve_3(dr, jac, resid);
408
409 fac = 1, flags = 0;
410 for (d = 0; d < 3; ++d)
411 {
412 double nr = r0[d]+dr[d];
413 if ((nr-bnd[2*d])*(bnd[2*d+1]-nr) >= 0)
414 {
415 continue;
416 }
417 if (nr < bnd[2*d])
418 {
419 double f = (bnd[2*d]-r0[d]) / dr[d];
420 if (f < fac)
421 {
422 fac = f, flags = 1u << (2*d);
423 }
424 }
425 else
426 {
427 double f = (bnd[2*d+1]-r0[d]) / dr[d];
428 if (f < fac)
429 {
430 fac = f, flags = 2u << (2*d);
431 }
432 }
433 }
434
435 if (flags == 0)
436 {
437 goto newton_vol_fin;
438 }
439
440 for (d = 0; d < 3; ++d)
441 {
442 dr[d] *= fac;
443 }
444
445newton_vol_face :
446 {
447 const int fi = face_index(flags);
448 const int dn = fi >> 1, d1 = plus_1_mod_3(dn), d2 = plus_2_mod_3(dn);
449 double drc[2], facc = 1;
450 int new_flags = 0;
451 double ress[3], y[2], JtJ[3];
452 ress[0] = resid[0]-(jac[0]*dr[0]+jac[1]*dr[1]+jac[2]*dr[2]);
453 ress[1] = resid[1]-(jac[3]*dr[0]+jac[4]*dr[1]+jac[5]*dr[2]);
454 ress[2] = resid[2]-(jac[6]*dr[0]+jac[7]*dr[1]+jac[8]*dr[2]);
455 /* y = J_u^T res */
456 y[0] = jac[d1]*ress[0]+jac[3+d1]*ress[1]+jac[6+d1]*ress[2];
457 y[1] = jac[d2]*ress[0]+jac[3+d2]*ress[1]+jac[6+d2]*ress[2];
458 /* JtJ = J_u^T J_u */
459 JtJ[0] = jac[d1]*jac[d1]+jac[3+d1]*jac[3+d1] +
460 jac[6+d1]*jac[6 +d1];
461 JtJ[1] = jac[d1]*jac[d2]+jac[3+d1]*jac[3+d2] +
462 jac[6+d1]*jac[6+d2];
463 JtJ[2] = jac[d2]*jac[d2]+jac[3+d2]*jac[3+d2] +
464 jac[6+d2]*jac[6+d2];
465 lin_solve_sym_2(drc, JtJ, y);
466#define CHECK_CONSTRAINT(drcd, d3) \
467{ \
468 const double rz = r0[d3]+dr[d3], lb = bnd[2*d3], ub = bnd[2*d3+1]; \
469 const double delta = drcd, nr = r0[d3]+(dr[d3]+delta); \
470 if ((nr-lb)*(ub-nr) < 0) { \
471 if (nr < lb) { \
472 double f = (lb-rz) / delta; \
473 if (f < fac) { \
474 fac = f; new_flags = 1u << (2*d3); \
475 } \
476 } \
477 else { \
478 double f = (ub-rz) / delta; \
479 if (f < fac) { \
480 fac = f; new_flags = 2u << (2*d3); \
481 } \
482 } \
483 } \
484}
485 CHECK_CONSTRAINT(drc[0], d1);
486 CHECK_CONSTRAINT(drc[1], d2);
487 dr[d1] += facc*drc[0], dr[d2] += facc*drc[1];
488 if (new_flags == 0)
489 {
490 goto newton_vol_fin;
491 }
492 flags |= new_flags;
493 }
494
495newton_vol_edge :
496 {
497 const int ei = edge_index(flags);
498 const int de = ei >> 2;
499 double facc = 1;
500 int new_flags = 0;
501 double ress[3], y, JtJ, drc;
502 ress[0] = resid[0]-(jac[0]*dr[0]+jac[1]*dr[1]+jac[2]*dr[2]);
503 ress[1] = resid[1]-(jac[3]*dr[0]+jac[4]*dr[1]+jac[5]*dr[2]);
504 ress[2] = resid[2]-(jac[6]*dr[0]+jac[7]*dr[1]+jac[8]*dr[2]);
505 /* y = J_u^T res */
506 y = jac[de]*ress[0]+jac[3+de]*ress[1]+jac[6+de]*ress[2];
507 /* JtJ = J_u^T J_u */
508 JtJ = jac[de]*jac[de]+jac[3+de]*jac[3+de] +
509 jac[6+de]*jac[6+de];
510 drc = y / JtJ;
511 CHECK_CONSTRAINT(drc, de);
512#undef CHECK_CONSTRAINT
513 dr[de] += facc*drc;
514 flags |= new_flags;
515 goto newton_vol_relax;
516 }
517
518 /* check and possibly relax constraints */
519newton_vol_relax :
520 {
521 const int old_flags = flags;
522 double ress[3], y[3];
523 /* res := res_0-J dr */
524 ress[0] = resid[0]-(jac[0]*dr[0]+jac[1]*dr[1]+jac[2]*dr[2]);
525 ress[1] = resid[1]-(jac[3]*dr[0]+jac[4]*dr[1]+jac[5]*dr[2]);
526 ress[2] = resid[2]-(jac[6]*dr[0]+jac[7]*dr[1]+jac[8]*dr[2]);
527 /* y := J^T res */
528 y[0] = jac[0]*ress[0]+jac[3]*ress[1]+jac[6]*ress[2];
529 y[1] = jac[1]*ress[0]+jac[4]*ress[1]+jac[7]*ress[2];
530 y[2] = jac[2]*ress[0]+jac[5]*ress[1]+jac[8]*ress[2];
531 for (int dd = 0; dd < 3; ++dd)
532 {
533 int f = flags >> (2*dd) & 3u;
534 if (f)
535 {
536 dr[dd] = bnd[2*dd+(f-1)]-r0[dd];
537 if (dr[dd]*y[dd] < 0)
538 {
539 flags &= ~(3u << (2*dd));
540 }
541 }
542 }
543 if (flags == old_flags)
544 {
545 goto newton_vol_fin;
546 }
547 switch (num_constrained(flags))
548 {
549 case 1:
550 goto newton_vol_face;
551 case 2:
552 goto newton_vol_edge;
553 }
554 }
555
556newton_vol_fin:
557 flags &= mask;
558 if (fabs(dr[0])+fabs(dr[1])+fabs(dr[2]) < tol)
559 {
560 flags |= CONVERGED_FLAG;
561 }
562 {
563 const double res0 = resid[0]-(jac[0]*dr[0]+jac[1]*dr[1] +
564 jac[2]*dr[2]);
565 const double res1 = resid[1]-(jac[3]*dr[0]+jac[4]*dr[1] +
566 jac[5]*dr[2]);
567 const double res2 = resid[2]-(jac[6]*dr[0]+jac[7]*dr[1] +
568 jac[8]*dr[2]);
569 res->dist2p = resid[0]*resid[0]+resid[1]*resid[1] +
570 resid[2]*resid[2] -
571 (res0*res0+res1*res1+res2*res2);
572 }
573 for (int dd = 0; dd < 3; ++dd)
574 {
575 int f = flags >> (2*dd) & 3u;
576 res->r[dd] = f == 0 ? r0[dd]+dr[dd] : (f == 1 ? -1 : 1);
577 }
578 res->flags = flags | ((p->flags & FLAG_MASK) << 7);
579}
580
581// Full Newton solve on the face. One of r/s/t is constrained.
582static MFEM_HOST_DEVICE void newton_face(findptsPt *const res,
583 const double jac[9],
584 const double rhes[3],
585 const double resid[3],
586 const int d1,
587 const int d2,
588 const int dn,
589 const int flags,
590 const findptsPt *const p,
591 const double tol)
592{
593 const double tr = p->tr;
594 double bnd[4];
595 double r[2], dr[2] = {0, 0};
596 int mask, new_flags;
597 double v, tv;
598 int i;
599 double A[3], y[2], r0[2];
600 /* A = J^T J-resid_d H_d */
601 A[0] = jac[d1]*jac[d1]+jac[3+d1]*jac[3+d1] +
602 jac[6+d1]*jac[6+d1]-rhes[0];
603 A[1] = jac[d1]*jac[d2]+jac[3+d1]*jac[3+d2] +
604 jac[6+d1]*jac[6+d2]-rhes[1];
605 A[2] = jac[d2]*jac[d2]+jac[3+d2]*jac[3+d2] +
606 jac[6+d2]*jac[6+d2]-rhes[2];
607 /* y = J^T r */
608 y[0] = jac[d1]*resid[0]+jac[3+d1]*resid[1]+jac[6+d1]*resid[2];
609 y[1] = jac[d2]*resid[0]+jac[3+d2]*resid[1]+jac[6+d2]*resid[2];
610 r0[0] = p->r[d1];
611 r0[1] = p->r[d2];
612
613 new_flags = flags;
614 mask = 0x3fu;
615 if (r0[0]-tr > -1)
616 {
617 bnd[0] = -tr;
618 mask ^= 1u;
619 }
620 else
621 {
622 bnd[0] = -1-r0[0];
623 }
624 if (r0[0]+tr < 1)
625 {
626 bnd[1] = tr;
627 mask ^= 2u;
628 }
629 else
630 {
631 bnd[1] = 1-r0[0];
632 }
633 if (r0[1]-tr > -1)
634 {
635 bnd[2] = -tr;
636 mask ^= 1u << 2;
637 }
638 else
639 {
640 bnd[2] = -1-r0[1];
641 }
642 if (r0[1]+tr < 1)
643 {
644 bnd[3] = tr;
645 mask ^= 2u << 2;
646 }
647 else
648 {
649 bnd[3] = 1-r0[1];
650 }
651
652 if (A[0]+A[2] <= 0 || A[0]*A[2] <= A[1]*A[1])
653 {
654 goto newton_face_constrained;
655 }
656 lin_solve_sym_2(dr, A, y);
657
658#define EVAL(r, s) -(y[0]*r+y[1]*s)+(r*A[0]*r+(2*r*A[1]+s*A[2])*s) / 2
659 if ((dr[0]-bnd[0])*(bnd[1]-dr[0]) >= 0 &&
660 (dr[1]-bnd[2])*(bnd[3]-dr[1]) >= 0)
661 {
662 r[0] = r0[0]+dr[0], r[1] = r0[1]+dr[1];
663 v = EVAL(dr[0], dr[1]);
664 goto newton_face_fin;
665 }
666newton_face_constrained:
667 v = EVAL(bnd[0], bnd[2]);
668 i = 1u | (1u << 2);
669 tv = EVAL(bnd[1], bnd[2]);
670 if (tv < v)
671 {
672 v = tv;
673 i = 2u | (1u << 2);
674 }
675 tv = EVAL(bnd[0], bnd[3]);
676 if (tv < v)
677 {
678 v = tv;
679 i = 1u | (2u << 2);
680 }
681 tv = EVAL(bnd[1], bnd[3]);
682 if (tv < v)
683 {
684 v = tv;
685 i = 2u | (2u << 2);
686 }
687 if (A[0] > 0)
688 {
689 double drc;
690 drc = (y[0]-A[1]*bnd[2]) / A[0];
691 if ((drc-bnd[0])*(bnd[1]-drc) >= 0 && (tv = EVAL(drc, bnd[2])) < v)
692 {
693 v = tv;
694 i = 1u << 2;
695 dr[0] = drc;
696 }
697 drc = (y[0]-A[1]*bnd[3]) / A[0];
698 if ((drc-bnd[0])*(bnd[1]-drc) >= 0 && (tv = EVAL(drc, bnd[3])) < v)
699 {
700 v = tv;
701 i = 2u << 2;
702 dr[0] = drc;
703 }
704 }
705 if (A[2] > 0)
706 {
707 double drc;
708 drc = (y[1]-A[1]*bnd[0]) / A[2];
709 if ((drc-bnd[2])*(bnd[3]-drc) >= 0 && (tv = EVAL(bnd[0], drc)) < v)
710 {
711 v = tv;
712 i = 1u;
713 dr[1] = drc;
714 }
715 drc = (y[1]-A[1]*bnd[1]) / A[2];
716 if ((drc-bnd[2])*(bnd[3]-drc) >= 0 && (tv = EVAL(bnd[1], drc)) < v)
717 {
718 v = tv;
719 i = 2u;
720 dr[1] = drc;
721 }
722 }
723#undef EVAL
724
725 {
726 int dir[2];
727 dir[0] = d1;
728 dir[1] = d2;
729 for (int d = 0; d < 2; ++d)
730 {
731 const int f = i >> (2*d) & 3u;
732 if (f == 0)
733 {
734 r[d] = r0[d]+dr[d];
735 }
736 else
737 {
738 if ((f & (mask >> (2*d))) == 0)
739 {
740 r[d] = r0[d]+(f == 1 ? -tr : tr);
741 }
742 else
743 {
744 r[d] = (f == 1 ? -1 : 1);
745 new_flags |= f << (2*dir[d]);
746 }
747 }
748 }
749 }
750newton_face_fin:
751 res->dist2p = -2*v;
752 dr[0] = r[0]-p->r[d1];
753 dr[1] = r[1]-p->r[d2];
754 if (fabs(dr[0])+fabs(dr[1]) < tol)
755 {
756 new_flags |= CONVERGED_FLAG;
757 }
758 res->r[dn] = p->r[dn];
759 res->r[d1] = r[0];
760 res->r[d2] = r[1];
761 res->flags = new_flags | ((p->flags & FLAG_MASK) << 7);
762}
763
764// Full Newton solve on the edge. Two of r/s/t are constrained.
765static MFEM_HOST_DEVICE inline void newton_edge(findptsPt *const res,
766 const double jac[9],
767 const double rhes,
768 const double resid[3],
769 const int de,
770 const int dn1,
771 const int dn2,
772 int flags,
773 const findptsPt *const p,
774 const double tol)
775{
776 const double tr = p->tr;
777 /* A = J^T J-resid_d H_d */
778 const double A = jac[de]*jac[de]+jac[3+de]*jac[3+de]+jac[6+de] *
779 jac[6+de]-rhes;
780 /* y = J^T r */
781 const double y = jac[de]*resid[0]+jac[3+de]*resid[1]+jac[6+de] *
782 resid[2];
783
784 const double oldr = p->r[de];
785 double dr, nr, tdr, tnr;
786 double v, tv;
787 int new_flags = 0, tnew_flags = 0;
788
789#define EVAL(dr) (dr*A-2*y)*dr
790
791 /* if A is not SPD, quadratic model has no minimum */
792 if (A > 0)
793 {
794 dr = y / A;
795 nr = oldr+dr;
796 if (fabs(dr) < tr && fabs(nr) < 1)
797 {
798 v = EVAL(dr);
799 goto newton_edge_fin;
800 }
801 }
802
803 if ((nr = oldr-tr) > -1)
804 {
805 dr = -tr;
806 }
807 else
808 {
809 nr = -1;
810 dr = -1-oldr;
811 new_flags = flags | 1u << (2*de);
812 }
813 v = EVAL(dr);
814
815 if ((tnr = oldr+tr) < 1)
816 {
817 tdr = tr;
818 }
819 else
820 {
821 tnr = 1;
822 tdr = 1-oldr;
823 tnew_flags = flags | 2u << (2*de);
824 }
825 tv = EVAL(tdr);
826
827 if (tv < v)
828 {
829 nr = tnr;
830 dr = tdr;
831 v = tv;
832 new_flags = tnew_flags;
833 }
834
835newton_edge_fin:
836 /* check convergence */
837 if (fabs(dr) < tol)
838 {
839 new_flags |= CONVERGED_FLAG;
840 }
841 res->r[de] = nr;
842 res->r[dn1] = p->r[dn1];
843 res->r[dn2] = p->r[dn2];
844 res->dist2p = -v;
845 res->flags = flags | new_flags | ((p->flags & FLAG_MASK) << 7);
846#undef EVAL
847}
848
849// Find closest mesh node to the sought point.
850static MFEM_HOST_DEVICE void seed_j(const double *elx[3],
851 const double x[3],
852 const double *z,//GLL point locations [-1, 1]
853 double *dist2,
854 double *r[3],
855 const int j,
856 const int pN) // assumes j < pN
857{
858 dist2[j] = HUGE_VAL;
859
860 double zr = z[j];
861 for (int l = 0; l < pN; ++l)
862 {
863 const double zt = z[l];
864 for (int k = 0; k < pN; ++k)
865 {
866 double zs = z[k];
867
868 const int jkl = j+k*pN+l*pN*pN;
869 double dx[3];
870 for (int d = 0; d < 3; ++d)
871 {
872 dx[d] = x[d]-elx[d][jkl];
873 }
874 const double dist2_jkl = l2norm2(dx);
875 if (dist2[j] > dist2_jkl)
876 {
877 dist2[j] = dist2_jkl;
878 r[0][j] = zr;
879 r[1][j] = zs;
880 r[2][j] = zt;
881 }
882 }
883 }
884}
885
886/* Compute contribution towards function value and its derivatives in each
887 reference direction. */
888static MFEM_HOST_DEVICE double tensor_ig3_j(double *g_partials,
889 const double *Jr,
890 const double *Dr,
891 const double *Js,
892 const double *Ds,
893 const double *Jt,
894 const double *Dt,
895 const double *u,
896 const int j,
897 const int pN)
898{
899 double uJtJs = 0.0;
900 double uDtJs = 0.0;
901 double uJtDs = 0.0;
902 for (int k = 0; k < pN; ++k)
903 {
904 double uJt = 0.0;
905 double uDt = 0.0;
906 for (int l = 0; l < pN; ++l)
907 {
908 uJt += u[j+k*pN+l*pN*pN]*Jt[l];
909 uDt += u[j+k*pN+l*pN*pN]*Dt[l];
910 }
911
912 uJtJs += uJt*Js[k];
913 uJtDs += uJt*Ds[k];
914 uDtJs += uDt*Js[k];
915 }
916
917 g_partials[0] = uJtJs*Dr[j];
918 g_partials[1] = uJtDs*Jr[j];
919 g_partials[2] = uDtJs*Jr[j];
920 return uJtJs*Jr[j];
921}
922
923template<int T_D1D = 0>
924static void FindPointsLocal3DKernel(const int npt,
925 const double tol,
926 const double *x,
927 const int point_pos_ordering,
928 const double *xElemCoord,
929 const int nel,
930 const double *wtend,
931 const double *boxinfo,
932 const int hash_n,
933 const double *hashMin,
934 const double *hashFac,
935 unsigned int *hashOffset,
936 unsigned int *const code_base,
937 unsigned int *const el_base,
938 double *const r_base,
939 double *const dist2_base,
940 const double *gll1D,
941 const double *lagcoeff,
942 const int pN = 0)
943{
944 const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
945 const int D1D = T_D1D ? T_D1D : pN;
946 const int p_NE = D1D*D1D*D1D;
947 MFEM_VERIFY(MD1 <= DofQuadLimits::MAX_D1D,
948 "Increase Max allowable polynomial order.");
949 MFEM_VERIFY(D1D != 0, "Polynomial order not specified.");
950#define MAXC(a, b) (((a) > (b)) ? (a) : (b))
951 const int nThreads = MAXC(D1D*DIM, 15);
952
953 mfem::forall_2D(npt, nThreads, 1, [=] MFEM_HOST_DEVICE (int i)
954 {
955 // 4 D1D for seed, 18D1D+12 for vol, 21D1D+15 for face, 3D1D+32 for edge.
956 constexpr int size1 = 21*MD1+15;
957 // 6D1D^2 for face, 15D1D for edge.
958 constexpr int size2 = MAXC(MD1*MD1*6, MD1*3*5);
959 //size depends on max of info for faces and edges
960 constexpr int size3 = MD1*MD1*MD1*DIM; // local element coordinates
961
962 MFEM_SHARED double r_workspace[size1];
963 MFEM_SHARED findptsPt el_pts[2];
964
965 MFEM_SHARED double constraint_workspace[size2];
966 MFEM_SHARED int face_edge_init;
967
968 MFEM_SHARED double elem_coords[MD1 <= 6 ? size3 : 1];
969
970 double *r_workspace_ptr;
971 findptsPt *fpt, *tmp;
972 MFEM_FOREACH_THREAD(j,x,nThreads)
973 {
974 r_workspace_ptr = r_workspace;
975 fpt = el_pts+0;
976 tmp = el_pts+1;
977 }
978 MFEM_SYNC_THREAD;
979
980 int id_x = point_pos_ordering == 0 ? i : i*DIM;
981 int id_y = point_pos_ordering == 0 ? i+npt : i*DIM+1;
982 int id_z = point_pos_ordering == 0 ? i+2*npt : i*DIM+2;
983 double x_i[3] = {x[id_x], x[id_y], x[id_z]};
984
985 unsigned int *code_i = code_base+i;
986 double *dist2_i = dist2_base+i;
987
988 //// map_points_to_els ////
990 for (int d = 0; d < DIM; ++d)
991 {
992 hash.bnd[d].min = hashMin[d];
993 hash.fac[d] = hashFac[d];
994 }
995 hash.hash_n = hash_n;
996 hash.offset = hashOffset;
997 const unsigned int hi = hash_index(&hash, x_i);
998 const unsigned int *elp = hash.offset+hash.offset[hi],
999 *const ele = hash.offset+hash.offset[hi+1];
1000 *code_i = CODE_NOT_FOUND;
1001 *dist2_i = HUGE_VAL;
1002
1003 for (; elp != ele; ++elp)
1004 {
1005 //elp
1006
1007 const int el = *elp;
1008
1009 // construct obbox_t on the fly from data
1010 obbox_t box;
1011 int n_box_ents = 3*DIM+DIM2;
1012 for (int idx = 0; idx < DIM; ++idx)
1013 {
1014 box.c0[idx] = boxinfo[n_box_ents*el+idx];
1015 box.x[idx].min = boxinfo[n_box_ents*el+DIM+idx];
1016 box.x[idx].max = boxinfo[n_box_ents*el+2*DIM+idx];
1017 }
1018
1019 for (int idx = 0; idx < DIM2; ++idx)
1020 {
1021 box.A[idx] = boxinfo[n_box_ents*el+3*DIM+idx];
1022 }
1023
1024 if (bbox_test(&box, x_i) < 0) { continue; }
1025
1026 //// findpts_local ////
1027 {
1028 // read element coordinates into shared memory
1029 if (MD1 <= 6)
1030 {
1031 MFEM_FOREACH_THREAD(j,x,D1D*DIM)
1032 {
1033 const int qp = j % D1D;
1034 const int d = j / D1D;
1035 for (int l = 0; l < D1D; ++l)
1036 {
1037 for (int k = 0; k < D1D; ++k)
1038 {
1039 const int jkl = qp+k*D1D+l*D1D*D1D;
1040 elem_coords[jkl+d*p_NE] =
1041 xElemCoord[jkl+el*p_NE+d*nel*p_NE];
1042 }
1043 }
1044 }
1045 MFEM_SYNC_THREAD;
1046 }
1047
1048 const double *elx[DIM];
1049 for (int d = 0; d < DIM; d++)
1050 {
1051 elx[d] = MD1<= 6 ? &elem_coords[d*p_NE] :
1052 xElemCoord+d*nel*p_NE+el*p_NE;
1053 }
1054
1055 //// findpts_el ////
1056 {
1057 MFEM_SYNC_THREAD;
1058 MFEM_FOREACH_THREAD(j,x,1)
1059 {
1060 fpt->dist2 = HUGE_VAL;
1061 fpt->dist2p = 0;
1062 fpt->tr = 1;
1063 face_edge_init = 0;
1064 }
1065 MFEM_FOREACH_THREAD(j,x,DIM)
1066 {
1067 fpt->x[j] = x_i[j];
1068 }
1069 MFEM_SYNC_THREAD;
1070
1071 //// seed ////
1072 {
1073 double *dist2_temp = r_workspace_ptr;
1074 double *r_temp[DIM];
1075 for (int d = 0; d < DIM; ++d)
1076 {
1077 r_temp[d] = dist2_temp+(1+d)*D1D;
1078 }
1079
1080 MFEM_FOREACH_THREAD(j,x,D1D)
1081 {
1082 seed_j(elx, x_i, gll1D, dist2_temp, r_temp, j, D1D);
1083 }
1084 MFEM_SYNC_THREAD;
1085
1086 MFEM_FOREACH_THREAD(j,x,1)
1087 {
1088 fpt->dist2 = HUGE_VAL;
1089 for (int jj = 0; jj < D1D; ++jj)
1090 {
1091 if (dist2_temp[jj] < fpt->dist2)
1092 {
1093 fpt->dist2 = dist2_temp[jj];
1094 for (int d = 0; d < DIM; ++d)
1095 {
1096 fpt->r[d] = r_temp[d][jj];
1097 }
1098 }
1099 }
1100 }
1101 MFEM_SYNC_THREAD;
1102 } //seed done
1103
1104 MFEM_FOREACH_THREAD(j,x,1)
1105 {
1106 tmp->dist2 = HUGE_VAL;
1107 tmp->dist2p = 0;
1108 tmp->tr = 1;
1109 tmp->flags = 0; // we do newton_vol regardless of seed.
1110 }
1111 MFEM_FOREACH_THREAD(j,x,DIM)
1112 {
1113 tmp->x[j] = fpt->x[j];
1114 tmp->r[j] = fpt->r[j];
1115 }
1116 MFEM_SYNC_THREAD;
1117
1118 for (int step = 0; step < 50; step++)
1119 {
1120 switch (num_constrained(tmp->flags & FLAG_MASK))
1121 {
1122 case 0: // findpt_vol
1123 {
1124 double *wtr = r_workspace_ptr;
1125 double *resid = wtr+6*D1D;
1126 double *jac = resid+3;
1127 double *resid_temp = jac+9;
1128 double *jac_temp = resid_temp+3*D1D;
1129
1130 MFEM_FOREACH_THREAD(j,x,D1D*DIM)
1131 {
1132 const int qp = j % D1D;
1133 const int d = j / D1D;
1134 lag_eval_first_der(wtr+2*d*D1D, tmp->r[d], qp,
1135 gll1D, lagcoeff, D1D);
1136 }
1137 MFEM_SYNC_THREAD;
1138
1139 MFEM_FOREACH_THREAD(j,x,D1D*DIM)
1140 {
1141 const int qp = j % D1D;
1142 const int d = j / D1D;
1143 double *idx = jac_temp+3*d+9*qp;
1144 resid_temp[d+qp*3] = tensor_ig3_j(idx,
1145 wtr,
1146 wtr+D1D,
1147 wtr+2*D1D,
1148 wtr+3*D1D,
1149 wtr+4*D1D,
1150 wtr+5*D1D,
1151 elx[d],
1152 qp, D1D);
1153 }
1154 MFEM_SYNC_THREAD;
1155
1156 MFEM_FOREACH_THREAD(l,x,3)
1157 {
1158 resid[l] = tmp->x[l];
1159 for (int j = 0; j < D1D; ++j)
1160 {
1161 resid[l] -= resid_temp[l+j*3];
1162 }
1163 }
1164 MFEM_FOREACH_THREAD(l,x,9)
1165 {
1166 jac[l] = 0;
1167 for (int j = 0; j < D1D; ++j)
1168 {
1169 jac[l] += jac_temp[l+j*9];
1170 }
1171 }
1172 MFEM_SYNC_THREAD;
1173
1174 MFEM_FOREACH_THREAD(l,x,1)
1175 {
1176 // if (l == 0)
1177 {
1178 if (!reject_prior_step_q(fpt, resid, tmp, tol))
1179 {
1180 newton_vol(fpt, jac, resid, tmp, tol);
1181 }
1182 }
1183 }
1184 MFEM_SYNC_THREAD;
1185 break;
1186 } //case 0
1187 case 1: // findpt_face
1188 {
1189 const int fi = face_index(tmp->flags & FLAG_MASK);
1190 const int dn = fi >> 1;
1191 const int d1 = plus_1_mod_3(dn), d2 = plus_2_mod_3(dn);
1192
1193 double *wt1 = r_workspace_ptr;
1194 double *resid = wt1+6*D1D;
1195 double *jac = resid+3;
1196 double *resid_temp = jac+9;
1197 double *jac_temp = resid_temp+3*D1D;
1198 double *hes = jac_temp+9*D1D;
1199 double *hes_temp = hes+3;
1200 MFEM_SYNC_THREAD;
1201
1202 MFEM_FOREACH_THREAD(j,x,D1D*2)
1203 {
1204 int dd[2];
1205 dd[0] = d1;
1206 dd[1] = d2;
1207 const int d = j / D1D;
1208 const int qp = j % D1D;
1209 lag_eval_second_der(wt1+3*d*D1D, tmp->r[dd[d]],
1210 qp, gll1D, lagcoeff, D1D);
1211 }
1212 MFEM_SYNC_THREAD;
1213
1214 double *J1 = wt1, *D1 = wt1+D1D;
1215 double *J2 = wt1+3*D1D, *D2 = J2+D1D;
1216 double *DD1 = D1+D1D, *DD2 = D2+D1D;
1217 findptsElemFace face;
1218
1219 MFEM_FOREACH_THREAD(j,x,D1D*DIM)
1220 {
1221 // utilizes first 3*D1D threads
1222 face = get_face(elx, wtend, fi, constraint_workspace,
1223 face_edge_init, j, D1D);
1224 }
1225 MFEM_SYNC_THREAD;
1226
1227 MFEM_FOREACH_THREAD(j,x,D1D*DIM)
1228 {
1229 if (j == 0) { face_edge_init = (1 << fi); }
1230 const int qp = j % D1D;
1231 const int d = j / D1D;
1232 const double *u = face.x[d];
1233 const double *du = face.dxdn[d];
1234 double sums_k[4] = {0.0, 0.0, 0.0, 0.0};
1235 for (int k = 0; k < D1D; ++k)
1236 {
1237 sums_k[0] += u[qp+k*D1D]*J2[k];
1238 sums_k[1] += u[qp+k*D1D]*D2[k];
1239 sums_k[2] += u[qp+k*D1D]*DD2[k];
1240 sums_k[3] += du[qp+k*D1D]*J2[k];
1241 }
1242
1243 resid_temp[3*qp+d] = sums_k[0]*J1[qp];
1244 jac_temp[9*qp+3*d+d1] = sums_k[0]*D1[qp];
1245 jac_temp[9*qp+3*d+d2] = sums_k[1]*J1[qp];
1246 jac_temp[9*qp+3*d+dn] = sums_k[3]*J1[qp];
1247 if (d == 0)
1248 {
1249 hes_temp[3*qp] = sums_k[0]*DD1[qp];
1250 hes_temp[3*qp+1] = sums_k[1]*D1[qp];
1251 hes_temp[3*qp+2] = sums_k[2]*J1[qp];
1252 }
1253 }
1254 MFEM_SYNC_THREAD;
1255
1256 MFEM_FOREACH_THREAD(l,x,3)
1257 {
1258 resid[l] = fpt->x[l];
1259 hes[l] = 0;
1260 for (int j = 0; j < D1D; ++j)
1261 {
1262 resid[l] -= resid_temp[l+j*3];
1263 hes[l] += hes_temp[l+3*j];
1264 }
1265 hes[l] *= resid[l];
1266 }
1267
1268 MFEM_FOREACH_THREAD(l,x,9)
1269 {
1270 jac[l] = 0;
1271 for (int j = 0; j < D1D; ++j)
1272 {
1273 jac[l] += jac_temp[l+j*9];
1274 }
1275 }
1276 MFEM_SYNC_THREAD;
1277
1278 MFEM_FOREACH_THREAD(l,x,1)
1279 {
1280 if (!reject_prior_step_q(fpt, resid, tmp, tol))
1281 {
1282 const double steep = resid[0]*jac[dn]+
1283 resid[1]*jac[3+dn]+
1284 resid[2]*jac[6+dn];
1285 if (steep*tmp->r[dn] < 0)
1286 {
1287 // relax constraint //
1288 newton_vol(fpt, jac, resid, tmp, tol);
1289 }
1290 else
1291 {
1292 newton_face(fpt, jac, hes, resid, d1,
1293 d2, dn, tmp->flags&FLAG_MASK,
1294 tmp, tol);
1295 }
1296 }
1297 }
1298 MFEM_SYNC_THREAD;
1299 break;
1300 }
1301 case 2: // findpt_edge
1302 {
1303 const int ei = edge_index(tmp->flags & FLAG_MASK);
1304 const int de = ei >> 2,
1305 dn1 = plus_1_mod_3(de),
1306 dn2 = plus_2_mod_3(de);
1307 int d_j[3];
1308 d_j[0] = de;
1309 d_j[1] = dn1;
1310 d_j[2] = dn2;
1311 const int hes_count = 2*3-1;
1312
1313 double *wt = r_workspace_ptr;
1314 double *resid = wt+3*D1D;
1315 double *jac = resid+3;
1316 double *hes_T = jac+9;
1317 double *hes = hes_T+hes_count*3;
1318 findptsElemEdge edge;
1319
1320 MFEM_FOREACH_THREAD(j,x,nThreads)
1321 {
1322 // utilizes first 3*D1D threads f
1323 edge = get_edge(elx, wtend, ei,
1324 constraint_workspace,
1325 face_edge_init, j,
1326 D1D);
1327 }
1328 MFEM_SYNC_THREAD;
1329
1330 const double *const *e_x[3+3] = {edge.x, edge.x,
1331 edge.dxdn1,
1332 edge.dxdn2,
1333 edge.d2xdn1,
1334 edge.d2xdn2
1335 };
1336
1337 MFEM_FOREACH_THREAD(j,x,D1D)
1338 {
1339 if (j == 0) { face_edge_init = (64 << ei); }
1340 lag_eval_second_der(wt, tmp->r[de], j, gll1D,
1341 lagcoeff, D1D);
1342 }
1343 MFEM_SYNC_THREAD;
1344
1345 MFEM_FOREACH_THREAD(j,x,hes_count*3)
1346 {
1347 const int d = j % 3;
1348 const int row = j / 3;
1349 if (j < (3+1)*3)
1350 {
1351 // resid and jac_T
1352 // [0, 1, 0, 0]
1353 double *wt_j = wt+(row == 1 ? D1D : 0);
1354 const double *x = e_x[row][d];
1355 double sum = 0.0;
1356 for (int k = 0; k < D1D; ++k)
1357 {
1358 // resid+3 == jac_T
1359 sum += wt_j[k]*x[k];
1360 }
1361 if (j < 3)
1362 {
1363 resid[j] = tmp->x[j]-sum;
1364 }
1365 else
1366 {
1367 jac[d*3+d_j[row-1]] = sum;
1368 }
1369 }
1370
1371 {
1372 // Hes_T is transposed version (i.e. in col major)
1373 // n1*[2, 1, 1, 0, 0]
1374 // j==1 => wt_j = wt+n1
1375 double *wt_j = wt+D1D*(2 - (row+1)/2);
1376 const double *x = e_x[row+1][d];
1377 hes_T[j] = 0.0;
1378 for (int k = 0; k < D1D; ++k)
1379 {
1380 hes_T[j] += wt_j[k]*x[k];
1381 }
1382 }
1383 }
1384 MFEM_SYNC_THREAD;
1385
1386 MFEM_FOREACH_THREAD(j,x,hes_count)
1387 {
1388 hes[j] = 0.0;
1389 for (int d = 0; d < 3; ++d)
1390 {
1391 hes[j] += resid[d]*hes_T[j*3+d];
1392 }
1393 }
1394 MFEM_SYNC_THREAD;
1395
1396 MFEM_FOREACH_THREAD(l,x,1)
1397 {
1398 // check prior step //
1399 if (!reject_prior_step_q(fpt, resid, tmp, tol))
1400 {
1401 // check constraint //
1402 double steep[3-1];
1403 for (int k = 0; k < 3-1; ++k)
1404 {
1405 int dn = d_j[k+1];
1406 steep[k] = 0;
1407 for (int d = 0; d < 3; ++d)
1408 {
1409 steep[k] += jac[dn+d*3]*resid[d];
1410 }
1411 steep[k] *= tmp->r[dn];
1412 }
1413 if (steep[0] < 0)
1414 {
1415 if (steep[1] < 0)
1416 {
1417 newton_vol(fpt, jac, resid, tmp, tol);
1418 }
1419 else
1420 {
1421 double rh[3];
1422 rh[0] = hes[0];
1423 rh[1] = hes[1];
1424 rh[2] = hes[3];
1425 newton_face(fpt, jac, rh, resid, de,
1426 dn1, dn2,
1427 tmp->flags&(3u<<(dn2*2)),
1428 tmp, tol);
1429 }
1430 }
1431 else
1432 {
1433 if (steep[1] < 0)
1434 {
1435 double rh[3];
1436 rh[0] = hes[4];
1437 rh[1] = hes[2];
1438 rh[2] = hes[0];
1439 newton_face(fpt, jac, rh, resid, dn2,
1440 de, dn1,
1441 tmp->flags&(3u<<(dn1*2)),
1442 tmp, tol);
1443 }
1444 else
1445 {
1446 newton_edge(fpt, jac, hes[0], resid,
1447 de, dn1, dn2,
1448 tmp->flags & FLAG_MASK,
1449 tmp, tol);
1450 }
1451 }
1452 }
1453 }
1454 MFEM_SYNC_THREAD;
1455 break;
1456 }
1457 case 3: // findpts_pt
1458 {
1459 MFEM_FOREACH_THREAD(j,x,1)
1460 {
1461 // if (j == 0)
1462 const int pi=point_index(tmp->flags & FLAG_MASK);
1463 const findptsElemPt gpt=get_pt(elx,wtend,pi,D1D);
1464 const double *const pt_x = gpt.x;
1465 const double *const jac = gpt.jac;
1466 const double *const hes = gpt.hes;
1467
1468 double resid[3], steep[3];
1469 for (int d = 0; d < 3; ++d)
1470 {
1471 resid[d] = fpt->x[d]-pt_x[d];
1472 }
1473 if (!reject_prior_step_q(fpt, resid, tmp, tol))
1474 {
1475 for (int d = 0; d < 3; ++d)
1476 {
1477 steep[d] = 0;
1478 for (int e = 0; e < 3; ++e)
1479 {
1480 steep[d] += jac[d+e*3]*resid[e];
1481 }
1482 steep[d] *= tmp->r[d];
1483 }
1484 int de, dn1, dn2, d1, d2, dn, hi0, hi1, hi2;
1485 if (steep[0] < 0)
1486 {
1487 if (steep[1] < 0)
1488 {
1489 if (steep[2] < 0)
1490 {
1491 newton_vol(fpt,jac,resid,tmp,tol);
1492 }
1493 else
1494 {
1495 d1 = 0; d2 = 1; dn = 2; hi0 = 0;
1496 hi1 = 1, hi2 = 3;
1497 double rh[3];
1498 rh[0] = resid[0]*hes[hi0] +
1499 resid[1]*hes[6+hi0] +
1500 resid[2]*hes[12+hi0];
1501 rh[1] = resid[0]*hes[hi1] +
1502 resid[1]*hes[6+hi1] +
1503 resid[2]*hes[12+hi1];
1504 rh[2] = resid[0]*hes[hi2] +
1505 resid[1]*hes[6+hi2] +
1506 resid[2]*hes[12+hi2];
1507 newton_face(fpt, jac, rh, resid,
1508 d1, d2, dn,
1509 (tmp->flags)&(3u<<(2*dn)),
1510 tmp, tol);
1511 }
1512 }
1513 else
1514 {
1515 if (steep[2] < 0)
1516 {
1517 d1 = 2; d2 = 0; dn = 1; hi0 = 5;
1518 hi1 = 2, hi2 = 0;
1519 double rh[3];
1520 rh[0] = resid[0]*hes[hi0] +
1521 resid[1]*hes[6+hi0] +
1522 resid[2]*hes[12+hi0];
1523 rh[1] = resid[0]*hes[hi1] +
1524 resid[1]*hes[6+hi1] +
1525 resid[2]*hes[12+hi1];
1526 rh[2] = resid[0]*hes[hi2] +
1527 resid[1]*hes[6+hi2] +
1528 resid[2]*hes[12+hi2];
1529 newton_face(fpt, jac, rh, resid,
1530 d1, d2, dn,
1531 (tmp->flags)&(3u<<(2*dn)),
1532 tmp, tol);
1533 }
1534 else
1535 {
1536 de = 0, dn1 = 1, dn2 = 2, hi0 = 0;
1537 const double rh =
1538 resid[0]*hes[hi0] +
1539 resid[1]*hes[6+hi0] +
1540 resid[2]*hes[12+hi0];
1541 newton_edge(fpt, jac, rh, resid,
1542 de, dn1, dn2,
1543 tmp->flags&(~(3u<<(2*de))),
1544 tmp, tol);
1545 }
1546 }
1547 }
1548 else
1549 {
1550 if (steep[1] < 0)
1551 {
1552 if (steep[2] < 0)
1553 {
1554 d1 = 1, d2 = 2, dn = 0;
1555 hi0 = 3, hi1 = 4, hi2 = 5;
1556 double rh[3];
1557 rh[0] = resid[0]*hes[hi0] +
1558 resid[1]*hes[6+hi0] +
1559 resid[2]*hes[12+hi0];
1560 rh[1] = resid[0]*hes[hi1] +
1561 resid[1]*hes[6+hi1] +
1562 resid[2]*hes[12+hi1];
1563 rh[2] = resid[0]*hes[hi2] +
1564 resid[1]*hes[6+hi2] +
1565 resid[2]*hes[12+hi2];
1566 newton_face(fpt, jac, rh, resid,
1567 d1, d2, dn,
1568 (tmp->flags)&(3u<<(2*dn)),
1569 tmp, tol);
1570 }
1571 else
1572 {
1573 de = 1, dn1 = 2, dn2 = 0, hi0 = 3;
1574 const double rh =
1575 resid[0]*hes[hi0] +
1576 resid[1]*hes[6+hi0] +
1577 resid[2]*hes[12+hi0];
1578 newton_edge(fpt, jac, rh, resid,
1579 de, dn1, dn2,
1580 tmp->flags&(~(3u<<(2*de))),
1581 tmp, tol);
1582 }
1583 }
1584 else
1585 {
1586 if (steep[2] < 0)
1587 {
1588 de = 2, dn1 = 0, dn2 = 1, hi0 = 5;
1589 const double rh =
1590 resid[0]*hes[hi0] +
1591 resid[1]*hes[6+hi0] +
1592 resid[2]*hes[12+hi0];
1593 newton_edge(fpt, jac, rh, resid,
1594 de, dn1, dn2,
1595 tmp->flags&(~(3u<<(2*de))),
1596 tmp, tol);
1597 }
1598 else
1599 {
1600 fpt->r[0] = tmp->r[0];
1601 fpt->r[1] = tmp->r[1];
1602 fpt->r[2] = tmp->r[2];
1603 fpt->dist2p = 0;
1604 fpt->flags =tmp->flags|CONVERGED_FLAG;
1605 }
1606 }
1607 }
1608 }
1609 }
1610 MFEM_SYNC_THREAD;
1611 break;
1612 } //case 3
1613 } //switch
1614 if (fpt->flags & CONVERGED_FLAG)
1615 {
1616 break;
1617 }
1618 MFEM_SYNC_THREAD;
1619 MFEM_FOREACH_THREAD(j,x,1)
1620 {
1621 *tmp = *fpt;
1622 }
1623 MFEM_SYNC_THREAD;
1624 } //for int step < 50
1625 } //findpts_el
1626
1627 bool converged_internal = (fpt->flags&FLAG_MASK)==CONVERGED_FLAG;
1628 if (*code_i == CODE_NOT_FOUND || converged_internal ||
1629 fpt->dist2 < *dist2_i)
1630 {
1631 MFEM_FOREACH_THREAD(j,x,1)
1632 {
1633 *(el_base+i) = el;
1634 *code_i = converged_internal ? CODE_INTERNAL :
1635 CODE_BORDER;
1636 *dist2_i = fpt->dist2;
1637 }
1638 MFEM_FOREACH_THREAD(j,x,DIM)
1639 {
1640 *(r_base+DIM*i+j) = fpt->r[j];
1641 }
1642 MFEM_SYNC_THREAD;
1643 if (converged_internal)
1644 {
1645 break;
1646 }
1647 }
1648 } //findpts_local
1649 } //elp
1650 });
1651#undef MAXC
1652}
1653
1655 int point_pos_ordering,
1656 Array<unsigned int> &code,
1657 Array<unsigned int> &elem, Vector &ref,
1658 Vector &dist, int npt)
1659{
1660 if (npt == 0)
1661 {
1662 return;
1663 }
1664 auto pp = point_pos.Read();
1665 auto pgslm = gsl_mesh.Read();
1666 auto pwt = DEV.wtend.Read();
1667 auto pbb = DEV.bb.Read();
1668 auto plhm = DEV.lh_min.Read();
1669 auto plhf = DEV.lh_fac.Read();
1670 auto plho = DEV.lh_offset.ReadWrite();
1671 auto pcode = code.Write();
1672 auto pelem = elem.Write();
1673 auto pref = ref.Write();
1674 auto pdist = dist.Write();
1675 auto pgll1d = DEV.gll1d.ReadWrite();
1676 auto plc = DEV.lagcoeff.Read();
1677 switch (DEV.dof1d)
1678 {
1679 case 2:
1680 FindPointsLocal3DKernel<2>(npt, DEV.newt_tol, pp, point_pos_ordering,
1681 pgslm, NE_split_total, pwt, pbb,
1682 DEV.lh_nx, plhm, plhf, plho,
1683 pcode, pelem, pref, pdist, pgll1d, plc);
1684 break;
1685 case 3:
1686 FindPointsLocal3DKernel<3>(npt, DEV.newt_tol, pp, point_pos_ordering,
1687 pgslm, NE_split_total, pwt, pbb,
1688 DEV.lh_nx, plhm, plhf, plho,
1689 pcode, pelem, pref, pdist, pgll1d, plc);
1690 break;
1691 case 4:
1692 FindPointsLocal3DKernel<4>(npt, DEV.newt_tol, pp, point_pos_ordering,
1693 pgslm, NE_split_total, pwt, pbb,
1694 DEV.lh_nx, plhm, plhf, plho,
1695 pcode, pelem, pref, pdist, pgll1d, plc);
1696 break;
1697 case 5:
1698 FindPointsLocal3DKernel<5>(npt, DEV.newt_tol, pp, point_pos_ordering,
1699 pgslm, NE_split_total, pwt, pbb,
1700 DEV.lh_nx, plhm, plhf, plho,
1701 pcode, pelem, pref, pdist, pgll1d, plc);
1702 break;
1703 default:
1704 FindPointsLocal3DKernel(npt, DEV.newt_tol, pp,
1705 point_pos_ordering, pgslm,
1706 NE_split_total, pwt, pbb,
1707 DEV.lh_nx, plhm, plhf, plho,
1708 pcode, pelem, pref, pdist, pgll1d, plc,
1709 DEV.dof1d);
1710 break;
1711 }
1712}
1713#undef pMax
1714#undef DIM2
1715#undef DIM
1716#undef CODE_INTERNAL
1717#undef CODE_BORDER
1718#undef CODE_NOT_FOUND
1719#else
1720void FindPointsGSLIB::FindPointsLocal3(const Vector &point_pos,
1721 int point_pos_ordering,
1722 Array<unsigned int> &code,
1723 Array<unsigned int> &elem, Vector &ref,
1724 Vector &dist, int npt) {};
1725#endif
1726} // namespace mfem
1727#endif //ifdef MFEM_USE_GSLIB
T * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:426
T * Write(bool on_dev=true)
Shortcut for mfem::Write(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:418
void FindPointsLocal3(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 3D.
struct mfem::FindPointsGSLIB::DevStruct DEV
Vector data type.
Definition vector.hpp:82
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:520
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:536
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:528
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
constexpr int DIM
MFEM_HOST_DEVICE T tr(const tensor< T, n, n > &A)
Returns the trace of a square matrix.
Definition tensor.hpp:1317
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 void lin_solve_sym_2(double x[2], const double A[3], const double y[2])
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)
Definition lor_mms.hpp:22
gslib::obbox_t< DIM > obbox_t
gslib::findptsLocalHashData_t< DIM > findptsLocalHashData_t
void forall_2D(int N, int X, int Y, lambda &&body)
Definition forall.hpp:1220
gslib::dbl_range_t dbl_range_t
std::function< real_t(const Vector &)> f(real_t mass_coeff)
Definition lor_mms.hpp:30
real_t p(const Vector &x, real_t t)
Array< unsigned int > lh_offset
Definition gslib.hpp:172