MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
findptssurf_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#ifdef MFEM_USE_GSLIB
16
17#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
18#pragma GCC diagnostic push
19#pragma GCC diagnostic ignored "-Wunused-function"
20#endif
21#include "gslib.h"
22#ifndef GSLIB_RELEASE_VERSION //gslib v1.0.7
23#define GSLIB_RELEASE_VERSION 10007
24#endif
25#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
26#pragma GCC diagnostic pop
27#endif
28
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 sDIM 3
36#define rDIM 2
37#define sDIM2 9
38
39struct findptsElementPoint_t
40{
41 double x[sDIM], r[rDIM], oldr[rDIM], dist2, dist2p, tr;
42 int flags;
43};
44
45struct findptsElementGEdge_t
46{
47 double *x[sDIM], *dxdn[sDIM], *d2xdn[sDIM];
48};
49
50struct findptsElementGPT_t
51{
52 double x[sDIM], jac[sDIM*rDIM], hes[sDIM*(rDIM+1)];
53};
54
55using dbl_range_t = gslib::dbl_range_t;
56using obbox_t = gslib::obbox_t<sDIM>;
57using findptsLocalHashData_t = gslib::findptsLocalHashData_t<sDIM>;
61using gslib::l2norm2;
64
65/* the bit structure of flags is CSSRR
66 the C bit --- 1<<4 --- is set when the point is converged
67 RR is 0 = 00b if r is unconstrained,
68 1 = 01b if r is constrained at -1
69 2 = 10b if r is constrained at +1
70 SS is similarly for s constraints
71 SSRR = smax,smin,rmax,rmin
72*/
73
74#define CONVERGED_FLAG (1u<<4)
75#define FLAG_MASK 0x1fu
76
77/* returns the number of constrained reference coordinates, max 2
78*/
79static MFEM_HOST_DEVICE inline int num_constrained(const int flags)
80{
81 const int y = (flags | flags>>1);
82 return (y & 1u) + (y>>2 & 1u);
83}
84
85/* returns (x+1)%2
86 */
87static MFEM_HOST_DEVICE inline int plus_1_mod_2(const int x)
88{
89 return x^1u;
90}
91
92/* assumes x = 1<<i, with i<4, returns i+1
93 * Gives index of the first bit set in x
94 */
95static MFEM_HOST_DEVICE inline int which_bit(const int x)
96{
97 const int y = x & 7u;
98 return (y-(y>>2)) | ((x-1)&4u);
99}
100
101/* Returns an index representing the edge:
102 * 0 for rmin, 1 for rmax, 2 for smin, 3 for smax
103 */
104static MFEM_HOST_DEVICE inline int edge_index(const int x)
105{
106 return which_bit(x) - 1;
107}
108
109static MFEM_HOST_DEVICE inline int point_index(const int x)
110{
111 return ((x>>1)&1u) | ((x>>2)&2u);
112}
113
114static MFEM_HOST_DEVICE inline void
115get_edge(const double *elx[3], const double *wtend, int ei,
116 int &side_init, int jidx, int pN, findptsElementGEdge_t &edge)
117{
118 // given edge index, compute normal and tangential directions
119 const int dn = ei>>1, //0 for rmin/rmax, 1 for smin/smax
120 de = plus_1_mod_2(dn); // 1 for rmin/rmax, 0 for smin/smax
121 const int side_n = ei&1, // 0 for rmin/smin, 1 for rmax/smax
122 side_n_offset = side_n*(pN-1); // 0 from rmin/smin, pN-1 for rmax/smax
123 const double *wt1 = wtend + 3*pN*side_n;
124
125 const int jj = jidx%pN;
126 const int dd = jidx/pN;
127 if (static_cast<unsigned>(side_init) != (1u << ei))
128 {
129 const int elx_stride[2] = {1,pN};
130#define ELX(d,j,k) elx[d][j*elx_stride[de] + k*elx_stride[dn]]
131
132 edge.x[dd][jj] = ELX(dd, jj, side_n_offset);
133 double sums_k[2] = {0,0};
134 for (int k=0; k<pN; ++k)
135 {
136 sums_k[0] += wt1[pN+k] * ELX(dd,jj,k);
137 sums_k[1] += wt1[2*pN+k] * ELX(dd,jj,k);
138 }
139 edge.dxdn[dd][jj] = sums_k[0];
140 edge.d2xdn[dd][jj] = sums_k[1];
141#undef ELX
142 }
143}
144
145static MFEM_HOST_DEVICE inline findptsElementGPT_t get_pt(const double *elx[3],
146 const double *wtend,
147 int pi,
148 int pN)
149{
150 const int side_n1 = pi&1,
151 side_n2 = (pi>>1)&1;
152 const int in1 = side_n1*(pN-1),
153 in2 = side_n2*(pN-1);
154 const int hes_stride = rDIM + 1; // rDIM + C^rDIM_2
155
156 findptsElementGPT_t pt;
157
158#define ELX(d,j,k) elx[d][j + k*pN]
159 for (int d=0; d<sDIM; ++d)
160 {
161 pt.x[d] = ELX(d,in1,in2);
162
163 // point to the start of 1st derivatives corresponding to whether r/s is constrained at -1 or 1.
164 const double *wt1 = wtend + pN + side_n1*3*pN;
165 const double *wt2 = wtend + pN + side_n2*3*pN;
166
167 for (int i=0; i<rDIM; ++i)
168 {
169 pt.jac[rDIM*d + i] = 0;
170 }
171 for (int i=0; i<hes_stride; ++i)
172 {
173 pt.hes[hes_stride*d + i] = 0;
174 }
175
176 for (int j=0; j<pN; ++j)
177 {
178 pt.jac[rDIM*d+0] += wt1[j] * ELX(d,j,in2);
179 pt.jac[rDIM*d+1] += wt2[j] * ELX(d,in1,j);
180
181 double sum_k = 0;
182 for (int k=0; k<pN; ++k)
183 {
184 sum_k += wt1[k] * ELX(d,k,j);
185 }
186 pt.hes[hes_stride*d+0] += wt1[pN+j] * ELX(d,j,in2);
187 pt.hes[hes_stride*d+1] += wt2[j] * sum_k;
188 pt.hes[hes_stride*d+2] += wt2[pN+j] * ELX(d,in1,j);
189 }
190#undef ELX
191 }
192 return pt;
193}
194
195/* check reduction in objective against prediction, and adjust
196 trust region radius (p->tr) accordingly;
197 may reject the prior step, returning 1; otherwise returns 0
198 sets out_pt->dist2, out_pt->index, out_pt->x, out_pt->oldr in any event,
199 leaving out_pt->r, out_pt->dr, out_pt->flags to be set when returning 0 */
200static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out_pt,
201 const double resid[3],
202 const findptsElementPoint_t *p,
203 const double tol)
204{
205 const double dist2 = l2norm2<sDIM>(resid);
206 const double decr = p->dist2 - dist2;
207 const double pred = p->dist2p;
208 for (int d=0; d<sDIM; ++d)
209 {
210 out_pt->x[d] = p->x[d];
211 }
212 for (int d=0; d<rDIM; ++d)
213 {
214 out_pt->oldr[d] = p->r[d];
215 }
216 out_pt->dist2 = dist2;
217 if (decr>=0.01*pred)
218 {
219 if (decr>=0.9*pred) // very good iteration
220 {
221 out_pt->tr = 2*p->tr;
222 }
223 else // good iteration
224 {
225 out_pt->tr = p->tr;
226 }
227 return false;
228 }
229 else // if the iteration in not good
230 {
231 /* reject step; note: the point will pass through this routine
232 again, and we set things up here so it gets classed as a
233 "very good iteration" --- this doubles the trust radius,
234 which is why we divide by 4 below */
235 double v0 = fabs(p->r[0] - p->oldr[0]),
236 v1 = fabs(p->r[1] - p->oldr[1]);
237 out_pt->tr = ( v0>v1 ? v0 : v1 )/4;
238 out_pt->dist2 = p->dist2;
239 out_pt->flags = p->flags >> 5;
240 out_pt->dist2p = -HUGE_VAL;
241 for (int d=0; d<rDIM; ++d)
242 {
243 out_pt->r[d] = p->oldr[d];
244 }
245 if (pred<dist2*tol)
246 {
247 out_pt->flags |= CONVERGED_FLAG;
248 }
249 return true;
250 }
251}
252
253/* minimize ||resid - jac * dr||_2, with |dr| <= tr, |r0+dr|<=1
254 (exact solution of trust region problem) */
255static MFEM_HOST_DEVICE void newton_face( findptsElementPoint_t *const out_pt,
256 const double jac[sDIM*rDIM],
257 const double rhes[3],
258 const double resid[sDIM],
259 const int flags,
260 const findptsElementPoint_t *const p,
261 const double tol )
262{
263 const double tr = p->tr;
264 double bnd[4];
265 double r[2], dr[2] = {0, 0};
266 int mask, new_flags;
267 double v, tv;
268 int i;
269 double A[3], y[2], r0[2];
270
271 /* A = J^T J - resid_d H_d
272 Technically A has one more term, but it is same as A[1],
273 since A is symmetric (both J^T J and H_d are symmetric )
274 */
275 A[0] = jac[0]*jac[0] + jac[2]*jac[2] + jac[4]*jac[4] - rhes[0];
276 A[1] = jac[0]*jac[1] + jac[2]*jac[3] + jac[4]*jac[5] - rhes[1];
277 A[2] = jac[1]*jac[1] + jac[3]*jac[3] + jac[5]*jac[5] - rhes[2];
278
279 /* y = J^T r */
280 y[0] = jac[0]*resid[0] + jac[2]*resid[1] + jac[4]*resid[2];
281 y[1] = jac[1]*resid[0] + jac[3]*resid[1] + jac[5]*resid[2];
282
283 r0[0] = p->r[0];
284 r0[1] = p->r[1];
285
286 new_flags = flags;
287 mask = 0xfu; // 1111 - MSB to LSB - smax,smin,rmax,rmin
288
289 // bnd stores limits of r based on the initial guess (r0) and trust region,
290 // and the mask is modified correspondingly.
291 // Example: r0 = [0.2, -0.3].. r0[0]-tr = 0.2-1 = -0.8.
292 // In this case the bounding box will be set to -0.8 for r=-1 edge of the face
293 // and the bit corresponding to rmin will be changed.
294
295 if (r0[0]-tr > -1) // unconstrained at r=-1, mask's 1st bit is set to 0
296 {
297 bnd[0] = -tr, mask ^= 1u;
298 }
299 else
300 {
301 bnd[0] = -1-r0[0];
302 }
303 if (r0[0]+tr < 1) // unconstrained at r=1, mask's 2nd bit is set to 0
304 {
305 bnd[1] = tr, mask ^= 2u;
306 }
307 else
308 {
309 bnd[1] = 1-r0[0];
310 }
311 if (r0[1]-tr > -1) // unconstrained at s=-1, mask's 3rd bit is set to 0
312 {
313 bnd[2] = -tr, mask ^= 1u<<2;
314 }
315 else
316 {
317 bnd[2] = -1-r0[1];
318 }
319 if (r0[1]+tr < 1) // unconstrained at s=1, mask's 4th bit is set to 0
320 {
321 bnd[3] = tr, mask ^= 2u << 2;
322 }
323 else
324 {
325 bnd[3] = 1 - r0[1];
326 }
327 // At this stage, mask has information on if the search space is constrained,
328 // and the specific edge of the face it is constrained to.
329 // bnd has the corresponding limits of the search space.
330
331 if (A[0]+A[2]<=0 || A[0]*A[2]<=A[1]*A[1])
332 {
333 goto newton_face_constrained;
334 }
335
336 lin_solve_sym_2(dr, A, y);
337
338#define EVAL(r,s) -(y[0]*r + y[1]*s) + (r*A[0]*r + (2*r*A[1] + s*A[2])*s)/2
339 if ((dr[0]-bnd[0])*(bnd[1]-dr[0])>=0 && (dr[1]-bnd[2])*(bnd[3]-dr[1])>=0)
340 {
341 r[0] = r0[0] + dr[0], r[1] = r0[1] + dr[1];
342 v = EVAL(dr[0], dr[1]);
343 goto newton_face_fin;
344 }
345
346newton_face_constrained:
347 v = EVAL(bnd[0], bnd[2]); // bound at r=-1 and s=-1
348 i = 1u|(1u<<2); // 0101b
349 tv = EVAL(bnd[1], bnd[2]); // bound at r=1 and s=-1
350 if (tv<v)
351 {
352 v = tv, i = 2u|(1u<<2); // i = 0110b
353 }
354 tv = EVAL(bnd[0], bnd[3]); // bound at r=-1 and s=1
355 if (tv<v)
356 {
357 v = tv, i = 1u|(2u<<2); // i = 1001b
358 }
359 tv = EVAL(bnd[1], bnd[3]); // bound at r=1 and s=1
360 if (tv<v)
361 {
362 v = tv, i = 2u|(2u<<2); // i = 1010b
363 }
364
365 if (A[0]>0) // for r[0] (i.e., r) ref coord
366 {
367 double drc;
368 drc = (y[0] - A[1]*bnd[2])/A[0];
369 if ( (drc-bnd[0])*(bnd[1]-drc)>=0 && // if drc lies within r=-1 and r=1
370 (tv=EVAL(drc,bnd[2]))<v )
371 {
372 // i = 0100b, relieve constraints at r=-1 or 1, and set constraints at s=-1
373 v = tv, i = 1u<<2, dr[0] = drc;
374 }
375 drc = (y[0] - A[1]*bnd[3])/A[0];
376 if ( (drc-bnd[0])*(bnd[1]-drc)>=0 && // if drc lies within r=-1 and r=1
377 (tv=EVAL(drc,bnd[3]))<v )
378 {
379 // i = 1000b, relieve constraints at r=-1 or 1, and set constraints at s=1
380 v = tv, i = 2u<<2, dr[0] = drc;
381 }
382 }
383 if (A[2]>0) // for r[1] (i.e., s) ref coord
384 {
385 double drc;
386 drc = (y[1] - A[1]*bnd[0])/A[2];
387 if ( (drc-bnd[2])*(bnd[3]-drc)>=0 && // if drc lies within s=-1 and s=1
388 (tv = EVAL(bnd[0], drc)) < v)
389 {
390 v = tv, i = 1u, dr[1] = drc; // i = 0001b, set constraints at r=-1
391 }
392 drc = (y[1] - A[1]*bnd[1])/A[2];
393 if ((drc-bnd[2])*(bnd[3]-drc)>=0 && // if drc lies within s=-1 and s=1
394 (tv = EVAL(bnd[1], drc))<v)
395 {
396 v = tv, i = 2u, dr[1] = drc; // i = 0010b, set constraints at r=1
397 }
398 }
399#undef EVAL
400
401 {
402 for (int d=0; d<rDIM; ++d)
403 {
404 // For d=0, f=0 if r is unconstrained; f=1 if r is constrained at -1; f=2 if r is constrained at 1
405 // For d=1, f=0 if s is unconstrained; f=1 if s is constrained at -1; f=2 if s is constrained at 1
406 const int f = (i>>2*d) & 3u;
407 if (f==0) // if r (or s) is unconstrained
408 {
409 r[d] = r0[d] + dr[d];
410 }
411 else // if r (or s) is constrained
412 {
413 if ( ( f&(mask>>(2*d)) ) == 0 )
414 {
415 r[d] = r0[d] + (f==1 ? -tr : tr);
416 }
417 else
418 {
419 r[d] = (f==1 ? -1 : 1), new_flags |= f<<(2*d);
420 }
421 }
422 }
423 }
424
425newton_face_fin:
426 out_pt->dist2p = -2*v;
427 dr[0] = r[0] - p->r[0];
428 dr[1] = r[1] - p->r[1];
429 if ( fabs(dr[0])+fabs(dr[1]) < tol)
430 {
431 new_flags |= CONVERGED_FLAG;
432 }
433 out_pt->r[0] = r[0], out_pt->r[1] = r[1];
434 out_pt->flags = new_flags | ((p->flags & FLAG_MASK)<<5);
435}
436
437static MFEM_HOST_DEVICE inline void newton_edge(findptsElementPoint_t *const
438 out_pt,
439 const double jac[sDIM*rDIM],
440 const double rhes,
441 const double resid[sDIM],
442 const int de,
443 const int dn,
444 int flags,
445 const findptsElementPoint_t *const p,
446 const double tol)
447{
448 const double tr = p->tr;
449 /* A = J^T J - resid_d H_d */
450 const double A = jac[de] *jac[de]
451 + jac[de+rDIM] *jac[de+rDIM]
452 + jac[de+2*rDIM]*jac[de+2*rDIM]
453 - rhes;
454 /* y = J^T r */
455 const double y = jac[de] *resid[0]
456 + jac[de+rDIM] *resid[1]
457 + jac[de+2*rDIM]*resid[2];
458
459 const double oldr = p->r[de];
460 double dr, nr, tdr, tnr;
461 double v, tv;
462 int new_flags = 0, tnew_flags = 0;
463
464#define EVAL(dr) (dr*A - 2*y)*dr
465
466 /* if A is not SPD, quadratic model has no minimum */
467 if (A>0)
468 {
469 dr = y/A;
470 // if dr is too small, set it to 0. Required since roundoff dr could cause
471 // fabs(newr)<1 to succeed when it shouldn't.
472 // FIXME: This check might be redundant since for 3d surface meshes, we have
473 // normal derivatives available and hence dr=0 truly means we are converged.
474 // we also check for dist2<dist2tol in newton iterations loop, which is a
475 // sureshot safeguard against false converged flag sets.
476 if (fabs(dr)<tol)
477 {
478 dr=0.0;
479 nr = oldr;
480 }
481 else
482 {
483 nr = oldr+dr;
484 }
485 if ( fabs(dr)<tr && fabs(nr)<1 )
486 {
487 v = EVAL(dr);
488 goto newton_edge_fin;
489 }
490 }
491
492 if ( (nr=oldr-tr)>-1 )
493 {
494 dr = -tr;
495 }
496 else
497 {
498 nr = -1, dr = -1-oldr, new_flags = flags | 1u<<2*de;
499 }
500 v = EVAL(dr);
501
502 if ( (tnr = oldr+tr)<1 )
503 {
504 tdr = tr;
505 }
506 else
507 {
508 tnr = 1, tdr = 1-oldr, tnew_flags = flags | 2u<<2*de;
509 }
510 tv = EVAL(tdr);
511
512 if (tv<v)
513 {
514 nr = tnr, dr = tdr, v = tv, new_flags = tnew_flags;
515 }
516
517newton_edge_fin:
518 /* check convergence */
519 if ( fabs(dr)<tol )
520 {
521 new_flags |= CONVERGED_FLAG;
522 }
523 out_pt->r[de] = nr;
524 out_pt->r[dn] = p->r[dn];
525 out_pt->dist2p = -v;
526 out_pt->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<5);
527#undef EVAL
528}
529
530static MFEM_HOST_DEVICE void seed_j(const double *elx[sDIM],
531 const double x[sDIM],
532 const double *z,
533 double *dist2,
534 double *r[rDIM],
535 const int j,
536 const int pN)
537{
538 dist2[j] = HUGE_VAL;
539 double zr = z[j];
540 for (int k=0; k<pN; ++k)
541 {
542 double zs = z[k];
543 const int jk = j + k*pN; // dof index
544 double dx[sDIM];
545 for (int d=0; d<sDIM; ++d)
546 {
547 dx[d] = x[d] - elx[d][jk];
548 }
549 const double dist2_jk = l2norm2(dx);
550 if (dist2[j]>dist2_jk)
551 {
552 dist2[j] = dist2_jk;
553 r[0][j] = zr;
554 r[1][j] = zs;
555 }
556 }
557}
558
559// global memory access of element coordinates.
560// Are the structs being stored in "local memory" or registers?
561template<int T_D1D = 0>
562static void FindPointsSurfLocal3DKernel(const int npt,
563 const double tol,
564 const double dist2tol,
565 const double *x,
566 const int point_pos_ordering,
567 const double *xElemCoord,
568 const int nel,
569 const double *wtend,
570 const double *boxinfo,
571 const bool obb_check,
572 const int hash_n,
573 const double *hashMin,
574 const double *hashFac,
575 unsigned int *hashOffset,
576 unsigned int *const code_base,
577 unsigned int *const el_base,
578 double *const r_base,
579 double *const dist2_base,
580 const double *gll1D,
581 const double *lagcoeff,
582 const int pN = 0)
583{
584 const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
585 const int D1D = T_D1D ? T_D1D : pN;
586 const int p_NE = D1D*D1D; // total nos. points in an element
587 MFEM_VERIFY(MD1<=DofQuadLimits::MAX_D1D,
588 "Increase Max allowable polynomial order.");
589 MFEM_VERIFY(pN<=DofQuadLimits::MAX_D1D,
590 "Increase Max allowable polynomial order.");
591 MFEM_VERIFY(D1D!=0, "Polynomial order not specified.");
592 const int nThreads = D1D*sDIM > 9 ? D1D*sDIM : 9;
593
594 mfem::forall_2D(npt, nThreads, 1, [=] MFEM_HOST_DEVICE (int i)
595 {
596 constexpr int size1 = 18*MD1 + 12;
597 constexpr int size2 = 9*MD1;
598 constexpr int size3 = MD1*MD1*sDIM; // local element coordinates
599
600 MFEM_SHARED double r_workspace[size1];
601 MFEM_SHARED findptsElementPoint_t el_pts[2];
602
603 MFEM_SHARED double constraint_workspace[size2];
604 MFEM_SHARED int edge_init;
605
606 MFEM_SHARED double elem_coords[MD1 <= 6 ? size3 : 1];
607
608 double *r_workspace_ptr = r_workspace;
609 findptsElementPoint_t *fpt, *tmp;
610 fpt = el_pts + 0;
611 tmp = el_pts + 1;
612
613 int id_x = point_pos_ordering==0 ? i : i*sDIM;
614 int id_y = point_pos_ordering==0 ? npt+i : 1+i*sDIM;
615 int id_z = point_pos_ordering==0 ? 2*npt+i : 2+i*sDIM;
616 double x_i[3] = {x[id_x], x[id_y], x[id_z]};
617
618 unsigned int *code_i = code_base + i;
619 double *dist2_i = dist2_base + i;
620
621 //// map_points_to_els ////
623 for (int d=0; d<sDIM; ++d)
624 {
625 hash.bnd[d].min = hashMin[d];
626 hash.fac[d] = hashFac[d];
627 }
628 hash.hash_n = hash_n;
629 hash.offset = hashOffset;
630 const unsigned int hi = hash_index(&hash, x_i);
631 const unsigned int *elp = hash.offset + hash.offset[hi],
632 *const ele = hash.offset + hash.offset[hi+1];
633 *code_i = CODE_NOT_FOUND;
634 *dist2_i = HUGE_VAL;
635
636 for (; elp!=ele; ++elp)
637 {
638 const unsigned int el = *elp;
639
640 const int n_box_ents = obb_check ? (3*sDIM + sDIM2) : (2*sDIM);
641 bool pass_bb = true;
642 obbox_t box;
643 if (obb_check)
644 {
645 // construct obbox on the fly
646 for (int idx = 0; idx < sDIM; ++idx)
647 {
648 box.c0[idx] = boxinfo[n_box_ents*el + idx];
649 box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
650 box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
651 }
652
653 for (int idx = 0; idx < sDIM2; ++idx)
654 {
655 box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
656 }
657 pass_bb = (bbox_test(&box, x_i) >= 0);
658 }
659 else
660 {
661 for (int d = 0; d < sDIM; ++d)
662 {
663 box.x[d].min = boxinfo[n_box_ents*el + d];
664 box.x[d].max = boxinfo[n_box_ents*el + sDIM + d];
665 }
666 pass_bb = (AABB_test(&box, x_i) >= 0);
667 }
668
669 if (!pass_bb) { continue; }
670
671 //// findpts_local ////
672 {
673 if (MD1 <= 6)
674 {
675 MFEM_FOREACH_THREAD(j,x,D1D*sDIM)
676 {
677 const int qp = j % D1D;
678 const int d = j / D1D;
679 for (int k = 0; k < D1D; ++k)
680 {
681 const int jk = qp + k * D1D;
682 elem_coords[jk + d*p_NE] =
683 xElemCoord[jk + el*p_NE + d*p_NE*nel];
684 }
685 }
686 MFEM_SYNC_THREAD;
687 }
688
689 const double *elx[sDIM];
690 for (int d=0; d<sDIM; d++)
691 {
692 elx[d] = MD1<= 6 ? &elem_coords[d*p_NE] :
693 xElemCoord + d*nel*p_NE + el*p_NE;
694 }
695
696 MFEM_SYNC_THREAD;
697 //// findpts_el ////
698 {
699 MFEM_FOREACH_THREAD(j,x,1)
700 {
701
702 fpt->dist2 = HUGE_VAL;
703 fpt->dist2p = 0;
704 fpt->tr = 1.0;
705 edge_init = 0;
706 }
707 MFEM_FOREACH_THREAD(j,x,sDIM)
708 {
709 fpt->x[j] = x_i[j];
710 }
711 MFEM_SYNC_THREAD;
712
713 //// seed ////
714 {
715 double *dist2_temp = r_workspace_ptr;
716 double *r_temp[rDIM];
717 for (int d=0; d<rDIM; ++d)
718 {
719 r_temp[d] = dist2_temp+(1+d)*D1D;
720 }
721 MFEM_FOREACH_THREAD(j,x,D1D)
722 {
723 seed_j(elx, x_i, gll1D, dist2_temp, r_temp, j, D1D);
724 }
725 MFEM_SYNC_THREAD;
726
727 MFEM_FOREACH_THREAD(j,x,1)
728 {
729 fpt->dist2 = HUGE_VAL;
730 for (int jj = 0; jj < D1D; ++jj)
731 {
732 if (dist2_temp[jj] < fpt->dist2)
733 {
734 fpt->dist2 = dist2_temp[jj];
735 for (int d = 0; d < rDIM; ++d)
736 {
737 fpt->r[d] = r_temp[d][jj];
738 }
739 }
740 }
741 }
742 MFEM_SYNC_THREAD;
743 } //seed done
744
745 MFEM_FOREACH_THREAD(j,x,1)
746 {
747 tmp->dist2 = HUGE_VAL;
748 tmp->dist2p = 0;
749 tmp->tr = 1;
750 tmp->flags = 0; // we do newton_vol regardless of seed.
751 }
752 MFEM_FOREACH_THREAD(j,x,rDIM)
753 {
754 tmp->r[j] = fpt->r[j];
755 }
756 MFEM_FOREACH_THREAD(j,x,sDIM)
757 {
758 tmp->x[j] = fpt->x[j];
759 }
760 MFEM_SYNC_THREAD;
761
762 for (int step=0; step<50; step++)
763 {
764 switch (num_constrained(tmp->flags & FLAG_MASK))
765 {
766 case 0: // findpt_area
767 {
768 double *wt1 = r_workspace_ptr;
769 double *resid = wt1 + 6*D1D;
770 double *jac = resid + sDIM;
771 double *resid_temp = jac + sDIM*rDIM;
772 double *jac_temp = resid_temp + sDIM*D1D;
773
774 // size 3 for sDIM=3, d2f/dr2, d2f/drds, and d2f/ds2
775 double *hes = jac_temp + sDIM*rDIM*D1D;
776 double *hes_temp = hes + 3;
777 MFEM_SYNC_THREAD;
778
779 MFEM_FOREACH_THREAD(j,x,D1D*rDIM)
780 {
781 const int qp = j % D1D;
782 const int d = j / D1D;
783 lag_eval_second_der(wt1+3*d*D1D, tmp->r[d], qp,
784 gll1D, lagcoeff, D1D);
785 }
786 MFEM_SYNC_THREAD;
787
788 double *J1 = wt1, *D1 = wt1+D1D, *DD1 = D1+D1D;
789 double *J2 = wt1 + 3*D1D, *D2 = J2+D1D, *DD2 = D2+D1D;
790
791 MFEM_FOREACH_THREAD(j,x,D1D*sDIM)
792 {
793 const int qp = j % D1D;
794 const int d = j / D1D;
795 const double *u = elx[d];
796 double sums_k[3] = {0.0, 0.0, 0.0};
797 for (int k=0; k<D1D; ++k)
798 {
799 sums_k[0] += u[qp + k*D1D] * J2[k];
800 sums_k[1] += u[qp + k*D1D] * D2[k];
801 sums_k[2] += u[qp + k*D1D] * DD2[k];
802 }
803
804 resid_temp[sDIM*qp+d] = sums_k[0] * J1[qp];
805 jac_temp[sDIM*rDIM*qp+rDIM*d+0] = sums_k[0]*D1[qp];
806 jac_temp[sDIM*rDIM*qp+rDIM*d+1] = sums_k[1]*J1[qp];
807 if (d==0)
808 {
809 hes_temp[3*qp + 0] = sums_k[0] * DD1[qp];
810 hes_temp[3*qp + 1] = sums_k[1] * D1[qp];
811 hes_temp[3*qp + 2] = sums_k[2] * J1[qp];
812 }
813 }
814 MFEM_SYNC_THREAD;
815
816 MFEM_FOREACH_THREAD(l,x,sDIM)
817 {
818 resid[l] = tmp->x[l];
819 for (int j=0; j<D1D; ++j)
820 {
821 resid[l] -= resid_temp[l + j*sDIM];
822 }
823 }
824 MFEM_FOREACH_THREAD(l,x,sDIM*rDIM)
825 {
826 jac[l] = 0;
827 for (int j=0; j<D1D; ++j)
828 {
829 jac[l] += jac_temp[l + j*sDIM*rDIM];
830 }
831 if (l<sDIM) // d2f/dr2, d2f/ds2, and d2f/drds
832 {
833 hes[l] = 0;
834 for (int j=0; j<D1D; ++j)
835 {
836 hes[l] += hes_temp[l + sDIM*j];
837 }
838 hes[l] *= resid[l];
839 }
840 }
841 MFEM_SYNC_THREAD;
842
843 MFEM_FOREACH_THREAD(l,x,1)
844 {
845 if (!reject_prior_step_q(fpt,resid,tmp,tol))
846 {
847 newton_face(fpt,jac,hes,resid,
848 (tmp->flags&CONVERGED_FLAG),
849 tmp,tol);
850 }
851 }
852 MFEM_SYNC_THREAD;
853 break;
854 }
855 case 1: // findpt_edge
856 {
857 const int ei = edge_index(tmp->flags & FLAG_MASK);
858 const int dn = ei>>1;
859 const int de = plus_1_mod_2(dn);
860 const int d_j[2] = {de,dn};
861 const int hes_count = 3;
862
863 double *wt = r_workspace_ptr;
864 double *resid = wt + 3*D1D;
865 double *jac = resid + sDIM;
866 double *hes_T = jac + sDIM*rDIM;
867 double *hes = hes_T + hes_count*sDIM;
868 findptsElementGEdge_t edge;
869 for (int d=0; d<sDIM; ++d)
870 {
871 edge.x[d] = constraint_workspace + d*D1D;
872 edge.dxdn[d] = constraint_workspace + d*D1D
873 + sDIM*D1D;
874 edge.d2xdn[d] = constraint_workspace + d*D1D
875 + 2*sDIM*D1D;
876 }
877
878 MFEM_FOREACH_THREAD(j,x,D1D*sDIM)
879 {
880 // One thread per physical component and edge DOF.
881 get_edge(elx, wtend, ei, edge_init, j, D1D, edge);
882 }
883 MFEM_SYNC_THREAD;
884
885 MFEM_FOREACH_THREAD(j,x,D1D)
886 {
887 if (j == 0) { edge_init = (1u << ei); }
888 lag_eval_second_der(wt,tmp->r[de],j,gll1D,
889 lagcoeff,D1D);
890 }
891 MFEM_SYNC_THREAD;
892
893 const double *const *e_x[4] = {edge.x, edge.x,
894 edge.dxdn, edge.d2xdn
895 };
896 MFEM_FOREACH_THREAD(j,x,hes_count*3)
897 {
898 const int d = j%sDIM; //0,1,2
899 const int row = j/sDIM; //0,1,2
900 {
901 double *wt_j = wt + (row==1 ? D1D : 0);
902 const double *x = e_x[row][d];
903 double sum = 0.0;
904 for (int k=0; k<D1D; ++k)
905 {
906 sum += wt_j[k]*x[k];
907 }
908 if (row==0) // j<sDIM
909 {
910 resid[j] = tmp->x[j] - sum;
911 }
912 else // row = 1, 2
913 {
914 jac[ d*rDIM + d_j[row-1] ] = sum;
915 }
916 }
917
918 {
919 // Hes_T is transposed version (i.e. in col major)
920 double *wt_j = wt + (2-row)*D1D;
921 hes_T[j] = 0.0;
922 for (int k=0; k<D1D; ++k)
923 {
924 hes_T[j] += wt_j[k] * e_x[row+1][d][k];
925 }
926 }
927 }
928 MFEM_SYNC_THREAD;
929
930 MFEM_FOREACH_THREAD(j,x,hes_count)
931 {
932 hes[j] = 0.0;
933 for (int d=0; d<sDIM; ++d)
934 {
935 hes[j] += resid[d] * hes_T[hes_count*j + d];
936 }
937 }
938 MFEM_SYNC_THREAD;
939
940 MFEM_FOREACH_THREAD(l,x,1)
941 {
942 if ( !reject_prior_step_q(fpt,resid,tmp,tol))
943 {
944 double steep = 0;
945 for (int d=0; d<sDIM; ++d)
946 {
947 steep += jac[d*rDIM + dn] * resid[d];
948 }
949 steep *= tmp->r[dn];
950 if (steep<0)
951 {
952 double face_hes[3] =
953 {
954 dn == 0 ? hes[2] : hes[0],
955 hes[1],
956 dn == 0 ? hes[0] : hes[2]
957 };
958 newton_face(fpt, jac, face_hes, resid,
959 tmp->flags & CONVERGED_FLAG,
960 tmp, tol);
961 }
962 else
963 {
964 newton_edge(fpt,jac,hes[0],resid,de,dn,tmp->flags&FLAG_MASK,tmp,tol);
965 }
966 }
967 }
968 MFEM_SYNC_THREAD;
969 break;
970 }
971 case 2: // findpts_pt
972 {
973 MFEM_FOREACH_THREAD(j,x,1)
974 {
975 const int pi=point_index(tmp->flags & FLAG_MASK);
976 const findptsElementGPT_t gpt=get_pt(elx,wtend,pi,D1D);
977 const double *const pt_x = gpt.x;
978 const double *const jac = gpt.jac;
979 const double *const hes = gpt.hes;
980
981 double resid[sDIM], steep[rDIM];
982 for (int d=0; d<sDIM; ++d)
983 {
984 resid[d] = fpt->x[d]-pt_x[d];
985 }
986 if (!reject_prior_step_q(fpt,resid,tmp,tol))
987 {
988 for (int d=0; d<rDIM; ++d)
989 {
990 steep[d] = 0;
991 for (int e=0; e<sDIM; ++e)
992 {
993 steep[d] += jac[e*rDIM+d] * resid[e];
994 }
995 steep[d] *= tmp->r[d];
996 }
997
998 int de, dn;
999 if (steep[0]<0)
1000 {
1001 if (steep[1]<0)
1002 {
1003 double rh[3];
1004 for (int rd=0; rd<3; ++rd)
1005 {
1006 rh[rd] = 0;
1007 for (int d=0; d<sDIM; ++d)
1008 {
1009 rh[rd] += resid[d] * hes[3*d + rd];
1010 }
1011 }
1012 newton_face(fpt,jac,rh,resid,
1013 (tmp->flags & CONVERGED_FLAG),
1014 tmp,tol);
1015 }
1016 else
1017 {
1018 de = 0, dn = 1;
1019 // hes index 0 is for d2x/dr2
1020 const double rh=resid[0] * hes[0] +
1021 resid[1] * hes[3+ 0] +
1022 resid[2] * hes[6 + 0];
1023 newton_edge(fpt,jac,rh,resid,de,dn,
1024 (tmp->flags & ~(3u<<2*de)),tmp,tol);
1025 }
1026 }
1027 else
1028 {
1029 if (steep[1]<0)
1030 {
1031 de = 1, dn = 0;
1032 // hes index 2 is for d2x/ds2
1033 const double rh = resid[0] * hes[2] +
1034 resid[1] * hes[5] +
1035 resid[2] * hes[8];
1036 newton_edge(fpt,jac,rh,resid,de,dn,
1037 (tmp->flags & ~(3u<<2*de)),tmp,tol);
1038 }
1039 else
1040 {
1041 fpt->r[0] = tmp->r[0];
1042 fpt->r[1] = tmp->r[1];
1043 fpt->dist2p = 0;
1044 fpt->flags = tmp->flags | CONVERGED_FLAG;
1045 }
1046 }
1047 }
1048 }
1049 MFEM_SYNC_THREAD;
1050 break;
1051 } // case 2
1052 } // switch
1053 if (fpt->flags & CONVERGED_FLAG)
1054 {
1055 break;
1056 }
1057 MFEM_SYNC_THREAD;
1058
1059 MFEM_FOREACH_THREAD(j,x,nThreads)
1060 {
1061 if (j==0)
1062 {
1063 *tmp = *fpt;
1064 }
1065 }
1066 MFEM_SYNC_THREAD;
1067 } // for step<50
1068 } // findpts_el
1069
1070 bool converged_internal =
1071 ((fpt->flags&FLAG_MASK)==CONVERGED_FLAG ) &&
1072 fpt->dist2<dist2tol;
1073 if (*code_i==CODE_NOT_FOUND || converged_internal ||
1074 fpt->dist2<*dist2_i)
1075 {
1076 MFEM_FOREACH_THREAD(j,x,1)
1077 {
1078 *(el_base+i) = el;
1079 *code_i = converged_internal ? CODE_INTERNAL :
1080 CODE_BORDER;
1081 *dist2_i = fpt->dist2;
1082 }
1083 MFEM_FOREACH_THREAD(j,x,rDIM)
1084 {
1085 *(r_base+rDIM*i+j) = fpt->r[j];
1086 }
1087 MFEM_SYNC_THREAD;
1088 if (converged_internal)
1089 {
1090 break;
1091 }
1092 }
1093 } // findpts_local
1094 } // elp
1095 });
1096}
1097
1099 int point_pos_ordering,
1100 Array<unsigned int> &code,
1101 Array<unsigned int> &elem,
1102 Vector &ref,
1103 Vector &dist,
1104 int npt)
1105{
1106 if (npt == 0)
1107 {
1108 return;
1109 }
1110 MFEM_VERIFY(dim == 2 && spacedim==3,"Function for 3D surfaces only");
1111 bool use_dev = point_pos.UseDevice();
1112 auto pp = point_pos.Read(use_dev);
1113 auto pgslm = gsl_mesh.Read(use_dev);
1114 auto pwt = DEV.wtend.Read(use_dev);
1115 auto pbb = DEV.bb.Read(use_dev);
1116 auto plhm = DEV.lh_min.Read(use_dev);
1117 auto plhf = DEV.lh_fac.Read(use_dev);
1118 auto plho = DEV.lh_offset.ReadWrite(use_dev);
1119 auto pcode = code.Write(use_dev);
1120 auto pelem = elem.Write(use_dev);
1121 auto pref = ref.Write(use_dev);
1122 auto pdist = dist.Write(use_dev);
1123 auto pgll1d = DEV.gll1d.ReadWrite(use_dev);
1124 auto plc = DEV.lagcoeff.Read(use_dev);
1125 double dist2tol = DEV.surf_dist_tol;
1126 const bool obb_chk = obb_check;
1127
1128 switch (DEV.dof1d)
1129 {
1130 case 2:
1131 FindPointsSurfLocal3DKernel<2>(npt, DEV.newt_tol, dist2tol,
1132 pp, point_pos_ordering, pgslm,
1133 NE_split_total, pwt, pbb, obb_chk,
1134 DEV.lh_nx, plhm, plhf, plho,
1135 pcode, pelem, pref, pdist,
1136 pgll1d, plc);
1137 break;
1138 case 3:
1139 FindPointsSurfLocal3DKernel<3>(npt, DEV.newt_tol, dist2tol,
1140 pp, point_pos_ordering, pgslm,
1141 NE_split_total, pwt, pbb, obb_chk,
1142 DEV.lh_nx, plhm, plhf, plho,
1143 pcode, pelem, pref, pdist,
1144 pgll1d, plc);
1145 break;
1146 case 4:
1147 FindPointsSurfLocal3DKernel<4>(npt, DEV.newt_tol, dist2tol,
1148 pp, point_pos_ordering, pgslm,
1149 NE_split_total, pwt, pbb, obb_chk,
1150 DEV.lh_nx, plhm, plhf, plho,
1151 pcode, pelem, pref, pdist,
1152 pgll1d, plc);
1153 break;
1154 default:
1155 FindPointsSurfLocal3DKernel(npt, DEV.newt_tol, dist2tol, pp,
1156 point_pos_ordering, pgslm,
1157 NE_split_total, pwt, pbb, obb_chk,
1158 DEV.lh_nx, plhm, plhf, plho,
1159 pcode, pelem, pref, pdist,
1160 pgll1d, plc, DEV.dof1d);
1161 break;
1162 }
1163}
1164
1165#undef sDIM2
1166#undef rDIM
1167#undef sDIM
1168#undef CODE_INTERNAL
1169#undef CODE_BORDER
1170#undef CODE_NOT_FOUND
1171#else
1172void FindPointsGSLIB::FindPointsSurfLocal3( const Vector &point_pos,
1173 int point_pos_ordering,
1174 Array<unsigned int> &code,
1175 Array<unsigned int> &elem,
1176 Vector &ref,
1177 Vector &dist,
1178 int npt ) {} ;
1179#endif
1180} // namespace mfem
1181
1182#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 FindPointsSurfLocal3(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 surface elements.
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 void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition vector.hpp:145
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:528
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 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)
MFEM_HOST_DEVICE double AABB_test(const obbox_t< SDIM > *const b, const double(&x)[SDIM])
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