MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
findpts_local_2.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
29
30namespace mfem
31{
32#if GSLIB_RELEASE_VERSION >= 10009
33#define CODE_INTERNAL 0
34#define CODE_BORDER 1
35#define CODE_NOT_FOUND 2
36#define DIM 2
37#define DIM2 4
38
39struct findptsElementPoint_t
40{
41 double x[DIM], r[DIM], oldr[DIM], dist2, dist2p, tr;
42 int flags;
43};
44
45struct findptsElementGEdge_t
46{
47 double *x[DIM], *dxdn[2];
48};
49
50struct findptsElementGPT_t
51{
52 double x[DIM], jac[DIM * DIM], hes[4];
53};
54
60using gslib::l2norm2;
63
64/*Solve Ax=y. A is row-major */
65static MFEM_HOST_DEVICE inline void lin_solve_2(double x[2], const double A[4],
66 const double y[2])
67{
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]);
71}
72
73/* the bit structure of flags is CSSRR
74 the C bit --- 1<<4 --- is set when the point is converged
75 RR is 0 = 00b if r is unconstrained,
76 1 = 01b if r is constrained at -1
77 2 = 10b if r is constrained at +1
78 SS is similarly for s constraints
79 SSRR = smax,smin,rmax,rmin
80*/
81#define CONVERGED_FLAG (1u<<4)
82#define FLAG_MASK 0x1fu // set all 5 bits to 1
83
84/* Determine number of constraints based on the CTTSSRR bits. */
85static MFEM_HOST_DEVICE inline int num_constrained(const int flags)
86{
87 const int y = flags | flags >> 1;
88 return (y&1u) + (y>>2 & 1u);
89}
90
91// Helper functions. Assumes x = 0, 1, or 2.
92static MFEM_HOST_DEVICE inline int plus_1_mod_2(const int x)
93{
94 return x ^ 1u;
95}
96// assumes x = 1 << i, with i < 4, returns i+1.
97static MFEM_HOST_DEVICE inline int which_bit(const int x)
98{
99 const int y = x & 7u;
100 return (y-(y>>2)) | ((x-1)&4u);
101}
102
103// Get edge index based on the SSRR bits.
104static MFEM_HOST_DEVICE inline int edge_index(const int x)
105{
106 return which_bit(x)-1;
107}
108
109// Gets vertex index based SSRR bits.
110static MFEM_HOST_DEVICE inline int point_index(const int x)
111{
112 return ((x>>1)&1u) | ((x>>2)&2u);
113}
114
115// compute (x,y) and (dxdn, dydn) data for all DOFs along the edge based on
116// edge index. ei=0..3 corresponding to rmin, rmax, smin, smax.
117static MFEM_HOST_DEVICE inline findptsElementGEdge_t
118get_edge(const double *elx[2], const double *wtend, int ei,
119 double *workspace,
120 int &side_init, int j, int pN) // Assumes j < pN
121{
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;
125
126 // location of derivatives based on whether we want at r/s=-1 or r/s=+1
127 // ei == 0 and 2 are constrained at -1, 1 and 3 are constrained at +1.
128 const double *wt1 = wtend + (ei%2==0 ? 0 : 1)* pN * 3 + pN;
129
130 for (int d = 0; d < 2; ++d)
131 {
132 edge.x[d] = workspace + d * pN; //x & y coordinates of DOFS along edge
133 edge.dxdn[d] = workspace + (2 + d) * pN; //dxdn and dydn at DOFs along edge
134 }
135
136 if (static_cast<unsigned>(side_init) != (1u << ei))
137 {
138#define ELX(d, j, k) elx[d][j + k * pN] // assumes lexicographic ordering
139 for (int d = 0; d < 2; ++d)
140 {
141 // copy nodal coordinates along the constrained edge
142 edge.x[d][j] = ELX(d, jidx, kidx);
143
144 // compute derivative in normal direction.
145 double sums_k = 0.0;
146 for (int k = 0; k < pN; ++k)
147 {
148 if (ei >= 2)
149 {
150 sums_k += wt1[k] * ELX(d, j, k);
151 }
152 else
153 {
154 sums_k += wt1[k] * ELX(d, k, j);
155 }
156 }
157 edge.dxdn[d][j] = sums_k;
158 }
159#undef ELX
160 }
161 return edge;
162}
163
164//pi=0, r=-1,s=-1
165//pi=1, r=+1,s=-1
166//pi=2, r=-1,s=+1
167//pi=3, r=+1,s=+1
168static MFEM_HOST_DEVICE inline findptsElementGPT_t get_pt(const double *elx[2],
169 const double *wtend,
170 int pi, int pN)
171{
172 findptsElementGPT_t pt;
173
174#define ELX(d, j, k) elx[d][j + k * pN]
175
176 int r_g_wt_offset = pi % 2 == 0 ? 0 : 1; //wtend offset for gradient
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;
180
181 pt.x[0] = ELX(0, jidx, kidx);
182 pt.x[1] = ELX(1, jidx, kidx);
183
184 pt.jac[0] = 0.0;
185 pt.jac[1] = 0.0;
186 pt.jac[2] = 0.0;
187 pt.jac[3] = 0.0;
188 for (int j = 0; j < pN; ++j)
189 {
190 //dx/dr
191 pt.jac[0] += wtend[3 * r_g_wt_offset * pN + pN + j] * ELX(0, j, kidx);
192
193 // dy/dr
194 pt.jac[2] += wtend[3 * r_g_wt_offset * pN + pN + j] * ELX(1, j, kidx);
195
196 // dx/ds
197 pt.jac[1] += wtend[3 * s_g_wt_offset * pN + pN + j] * ELX(0, kidx, j);
198
199 // dy/ds
200 pt.jac[3] += wtend[3 * s_g_wt_offset * pN + pN + j] * ELX(1, kidx, j);
201 }
202
203 pt.hes[0] = 0.0;
204 pt.hes[1] = 0.0;
205 pt.hes[2] = 0.0;
206 pt.hes[3] = 0.0;
207 for (int j = 0; j < pN; ++j)
208 {
209 //d2x/dr2
210 pt.hes[0] += wtend[3 * r_g_wt_offset * pN + 2*pN + j] * ELX(0, j, kidx);
211
212 // d2y/dr2
213 pt.hes[2] += wtend[3 * r_g_wt_offset * pN + 2*pN + j] * ELX(1, j, kidx);
214
215 // d2x/ds2
216 pt.hes[1] += wtend[3 * s_g_wt_offset * pN + 2*pN + j] * ELX(0, kidx, j);
217
218 // d2y/ds2
219 pt.hes[3] += wtend[3 * s_g_wt_offset * pN + 2*pN + j] * ELX(1, kidx, j);
220 }
221#undef ELX
222 return pt;
223}
224
225/* Check reduction in objective against prediction, and adjust trust region
226 radius (p->tr) accordingly. May reject the prior step, returning 1; otherwise
227 returns 0 sets res->dist2, res->index, res->x, res->oldr in any event,
228 leaving res->r, res->dr, res->flags to be set when returning 0 */
229static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *res,
230 const double resid[2],
231 const findptsElementPoint_t *p,
232 const double tol)
233{
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)
238 {
239 res->x[d] = p->x[d];
240 res->oldr[d] = p->r[d];
241 }
242 res->dist2 = dist2;
243 if (decr >= 0.01 * pred)
244 {
245 if (decr >= 0.9 * pred)
246 {
247 // very good iteration
248 res->tr = p->tr * 2;
249 }
250 else
251 {
252 // good iteration
253 res->tr = p->tr;
254 }
255 return false;
256 }
257 else
258 {
259 /* reject step; note: the point will pass through this routine
260 again, and we set things up here so it gets classed as a
261 "very good iteration" --- this doubles the trust radius,
262 which is why we divide by 4 below */
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)
268 {
269 res->r[d] = p->oldr[d];
270 }
271 res->flags = p->flags >> 5;
272 res->dist2p = -HUGE_VAL;
273 if (pred < dist2 * tol)
274 {
275 res->flags |= CONVERGED_FLAG;
276 }
277 return true;
278 }
279}
280
281/* Minimize 0.5||x* - x(r)||^2_2 using gradient-descent, with
282 |dr| <= tr and |r0+dr|<=1*/
283static MFEM_HOST_DEVICE void newton_area(findptsElementPoint_t *const res,
284 const double jac[4],
285 const double resid[2],
286 const findptsElementPoint_t *const p,
287 const double tol)
288{
289 const double tr = p->tr;
290 double bnd[4] = {-1, 1, -1, 1};
291 double r0[2];
292 double dr[2], fac;
293 int d, mask, flags;
294 r0[0] = p->r[0], r0[1] = p->r[1];
295
296 mask = 0xfu; // 1111 - MSB to LSB - smax,smin,rmax,rmin
297 for (d = 0; d < 2; ++d)
298 {
299 if (r0[d] - tr > -1)
300 {
301 bnd[2 * d] = r0[d] - tr, mask ^= 1u << (2 * d);
302 }
303 if (r0[d] + tr < 1)
304 {
305 bnd[2 * d + 1] = r0[d] + tr, mask ^= 2u << (2 * d);
306 }
307 }
308
309 // dr = Jac^-1*resid where resid = x^* - x(r)
310 lin_solve_2(dr, jac, resid);
311
312 fac = 1, flags = 0;
313 for (d = 0; d < 2; ++d)
314 {
315 double nr = r0[d] + dr[d];
316 if ((nr - bnd[2 * d]) * (bnd[2 * d + 1] - nr) >= 0)
317 {
318 continue;
319 }
320 if (nr < bnd[2 * d])
321 {
322 double f = (bnd[2 * d] - r0[d]) / dr[d];
323 if (f < fac)
324 {
325 fac = f, flags = 1u << (2 * d);
326 }
327 }
328 else
329 {
330 double f = (bnd[2 * d + 1] - r0[d]) / dr[d];
331 if (f < fac)
332 {
333 fac = f, flags = 2u << (2 * d);
334 }
335 }
336 }
337
338 if (flags == 0)
339 {
340 goto newton_area_fin;
341 }
342
343 for (d = 0; d < 2; ++d)
344 {
345 dr[d] *= fac;
346 }
347
348newton_area_edge :
349 {
350 const int ei = edge_index(flags);
351 const int dn = ei>>1, de = plus_1_mod_2(dn);
352 double facc = 1;
353 int new_flags = 0;
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]);
357 /* y = J_u^T res */
358 y = jac[de] * ress[0] + jac[2+de] * ress[1];
359 /* JtJ = J_u^T J_u */
360 JtJ = jac[de] * jac[de] + jac[2+de] * jac[2+de];
361 drc = y / JtJ;
362 {
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)
366 {
367 if (nr < lb)
368 {
369 double f = (lb-rz)/drc;
370 if (f < facc)
371 {
372 facc=f;
373 new_flags = 1u<<(2*de);
374 }
375 }
376 else
377 {
378 double f = (ub-rz)/drc;
379 if (f < facc)
380 {
381 facc=f;
382 new_flags = 2u<<(2*de);
383 }
384 }
385 }
386 }
387
388 dr[de] += facc * drc;
389 flags |= new_flags;
390 goto newton_area_relax;
391 }
392
393 /* check and possibly relax constraints */
394newton_area_relax :
395 {
396 const int old_flags = flags;
397 double ress[2], y[2];
398 /* res := res_0 - J dr */
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]);
401 /* y := J^T res */
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)
405 {
406 int f = flags >> (2 * dd) & 3u;
407 if (f)
408 {
409 dr[dd] = bnd[2 * dd + (f - 1)] - r0[dd];
410 if (dr[dd] * y[dd] < 0)
411 {
412 flags &= ~(3u << (2 * dd));
413 }
414 }
415 }
416 if (flags == old_flags)
417 {
418 goto newton_area_fin;
419 }
420 switch (num_constrained(flags))
421 {
422 case 1:
423 goto newton_area_edge;
424 }
425 }
426
427newton_area_fin:
428 flags &= mask;
429 if (fabs(dr[0]) + fabs(dr[1]) < tol)
430 {
431 flags |= CONVERGED_FLAG;
432 }
433 {
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);
438 }
439 for (int dd = 0; dd < 2; ++dd)
440 {
441 int f = flags >> (2 * dd) & 3u;
442 res->r[dd] = f == 0 ? r0[dd] + dr[dd] : (f == 1 ? -1 : 1);
443 }
444 res->flags = flags | ((p->flags & FLAG_MASK) << 5);
445}
446
447// Full Newton solve on the face. One of r/s/t is constrained.
448static MFEM_HOST_DEVICE inline void newton_edge(findptsElementPoint_t *const
449 res,
450 const double jac[4],
451 const double rhes,
452 const double resid[2],
453 const int de,
454 const int dn,
455 int flags,
456 const findptsElementPoint_t *const p,
457 const double tol)
458{
459 const double tr = p->tr;
460 /* A = J^T J - resid_d H_d */
461 const double A = jac[de] * jac[de] + jac[2 + de] * jac[2 + de] - rhes;
462 /* y = J^T r */
463 const double y = jac[de] * resid[0] + jac[2 + de] * resid[1];
464
465 const double oldr = p->r[de];
466 double dr, nr, tdr, tnr;
467 double v, tv;
468 int new_flags = 0, tnew_flags = 0;
469
470#define EVAL(dr) (dr * A - 2 * y) * dr
471
472 /* if A is not SPD, quadratic model has no minimum */
473 if (A > 0)
474 {
475 dr = y / A, nr = oldr + dr;
476 if (fabs(dr) < tr && fabs(nr) < 1)
477 {
478 v = EVAL(dr);
479 goto newton_edge_fin;
480 }
481 }
482
483 if ((nr = oldr - tr) > -1)
484 {
485 dr = -tr;
486 }
487 else
488 {
489 nr = -1, dr = -1 - oldr, new_flags = flags | 1u << (2 * de);
490 }
491 v = EVAL(dr);
492
493 if ((tnr = oldr + tr) < 1)
494 {
495 tdr = tr;
496 }
497 else
498 {
499 tnr = 1, tdr = 1 - oldr, tnew_flags = flags | 2u << (2 * de);
500 }
501 tv = EVAL(tdr);
502
503 if (tv < v)
504 {
505 nr = tnr, dr = tdr, v = tv, new_flags = tnew_flags;
506 }
507
508newton_edge_fin:
509 /* check convergence */
510 if (fabs(dr) < tol)
511 {
512 new_flags |= CONVERGED_FLAG;
513 }
514 res->r[de] = nr;
515 res->r[dn]=p->r[dn];
516 res->dist2p = -v;
517 res->flags = flags | new_flags | ((p->flags & FLAG_MASK) << 5);
518#undef EVAL
519}
520
521// Find closest mesh node to the sought point.
522static MFEM_HOST_DEVICE void seed_j(const double *elx[2],
523 const double x[2],
524 const double *z, //GLL point locations [-1, 1]
525 double *dist2,
526 double *r[2],
527 const int j,
528 const int pN)
529{
530 dist2[j] = HUGE_VAL;
531
532 double zr = z[j];
533 for (int k = 0; k < pN; ++k)
534 {
535 double zs = z[k];
536 const int jk = j + k * pN;
537 double dx[2];
538 for (int d = 0; d < 2; ++d)
539 {
540 dx[d] = x[d] - elx[d][jk];
541 }
542 const double dist2_jkl = l2norm2(dx);
543 if (dist2[j] > dist2_jkl)
544 {
545 dist2[j] = dist2_jkl;
546 r[0][j] = zr;
547 r[1][j] = zs;
548 }
549 }
550}
551
552/* Compute contribution towards function value and its derivatives in each
553 reference direction. */
554static MFEM_HOST_DEVICE double tensor_ig2_j(double *g_partials,
555 const double *Jr,
556 const double *Dr,
557 const double *Js,
558 const double *Ds,
559 const double *u,
560 const int j,
561 const int pN)
562{
563 double uJs = 0.0;
564 double uDs = 0.0;
565 for (int k = 0; k < pN; ++k)
566 {
567 uJs += u[j + k * pN] * Js[k];
568 uDs += u[j + k * pN] * Ds[k];
569 }
570
571 g_partials[0] = uJs * Dr[j];
572 g_partials[1] = uDs * Jr[j];
573 return uJs * Jr[j];
574}
575
576template<int T_D1D = 0>
577static void FindPointsLocal2DKernel(const int npt,
578 const double tol,
579 const double *x,
580 const int point_pos_ordering,
581 const double *xElemCoord,
582 const int nel,
583 const double *wtend,
584 const double *boxinfo,
585 const int hash_n,
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,
593 const double *gll1D,
594 const double *lagcoeff,
595 const int pN = 0)
596{
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;
605
606 mfem::forall_2D(npt, nThreads, 1, [=] MFEM_HOST_DEVICE (int i)
607 {
608 // 3D1D for seed, 10D1D+6 for area, 3D1D+9 for edge
609 constexpr int size1 = 10*MD1 + 6;
610 constexpr int size2 = MD1*4; // edge constraints
611 constexpr int size3 = MD1*MD1*DIM; // local element coordinates
612
613 MFEM_SHARED double r_workspace[size1];
614 MFEM_SHARED findptsElementPoint_t el_pts[2];
615
616 MFEM_SHARED double constraint_workspace[size2];
617 MFEM_SHARED int edge_init;
618
619 MFEM_SHARED double elem_coords[MD1 <= 6 ? size3 : 1];
620
621 double *r_workspace_ptr;
622 findptsElementPoint_t *fpt, *tmp;
623 MFEM_FOREACH_THREAD(j,x,nThreads)
624 {
625 r_workspace_ptr = r_workspace;
626 fpt = el_pts + 0;
627 tmp = el_pts + 1;
628 }
629 MFEM_SYNC_THREAD;
630
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]};
634
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;
639
640 // Initialize the code and dist
641 *code_i = CODE_NOT_FOUND;
642 *dist2_i = HUGE_VAL;
643
644 //// map_points_to_els ////
646 for (int d = 0; d < DIM; ++d)
647 {
648 hash.bnd[d].min = hashMin[d];
649 hash.fac[d] = hashFac[d];
650 }
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];
656
657 for (; elp != ele; ++elp)
658 {
659 //elp
660 const unsigned int el = *elp;
661
662 // construct obbox_t on the fly from data
663 obbox_t box;
664 int n_box_ents = 3*DIM + DIM2;
665
666 for (int idx = 0; idx < DIM; ++idx)
667 {
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];
671 }
672
673 for (int idx = 0; idx < DIM2; ++idx)
674 {
675 box.A[idx] = boxinfo[n_box_ents*el + 3*DIM + idx];
676 }
677
678 if (bbox_test(&box, x_i) < 0) { continue; }
679
680 //// findpts_local ////
681 {
682 // read element coordinates into shared memory
683 if (MD1 <= 6)
684 {
685 MFEM_FOREACH_THREAD(j,x,nThreads)
686 {
687 const int qp = j % D1D;
688 const int d = j / D1D;
689 for (int k = 0; k < D1D; ++k)
690 {
691 const int jk = qp + k * D1D;
692 elem_coords[jk + d*p_NE] =
693 xElemCoord[jk + el*p_NE + d*p_NEL];
694 }
695 }
696 MFEM_SYNC_THREAD;
697 }
698
699 const double *elx[DIM];
700 for (int d = 0; d < DIM; d++)
701 {
702 elx[d] = MD1<= 6 ? &elem_coords[d*p_NE] :
703 xElemCoord + d*p_NEL + el * p_NE;
704 }
705
706 //// findpts_el ////
707 {
708 MFEM_FOREACH_THREAD(j,x,1)
709 {
710 fpt->dist2 = HUGE_VAL;
711 fpt->dist2p = 0;
712 fpt->tr = 1;
713 edge_init = 0;
714 }
715 MFEM_FOREACH_THREAD(j,x,DIM)
716 {
717 fpt->x[j] = x_i[j];
718 }
719 MFEM_SYNC_THREAD;
720
721 //// seed ////
722 {
723 double *dist2_temp = r_workspace_ptr;
724 double *r_temp[DIM];
725 for (int d = 0; d < DIM; ++d)
726 {
727 r_temp[d] = dist2_temp + (1 + d) * D1D;
728 }
729
730 MFEM_FOREACH_THREAD(j,x,D1D)
731 {
732 seed_j(elx, x_i, gll1D, dist2_temp, r_temp, j, D1D);
733 }
734 MFEM_SYNC_THREAD;
735
736 MFEM_FOREACH_THREAD(j,x,1)
737 {
738 fpt->dist2 = HUGE_VAL;
739 for (int jj = 0; jj < D1D; ++jj)
740 {
741 if (dist2_temp[jj] < fpt->dist2)
742 {
743 fpt->dist2 = dist2_temp[jj];
744 for (int d = 0; d < DIM; ++d)
745 {
746 fpt->r[d] = r_temp[d][jj];
747 }
748 }
749 }
750 }
751 MFEM_SYNC_THREAD;
752 } //seed done
753
754 MFEM_FOREACH_THREAD(j,x,1)
755 {
756 tmp->dist2 = HUGE_VAL;
757 tmp->dist2p = 0;
758 tmp->tr = 1;
759 tmp->flags = 0;
760 }
761 MFEM_FOREACH_THREAD(j,x,DIM)
762 {
763 tmp->x[j] = fpt->x[j];
764 tmp->r[j] = fpt->r[j];
765 }
766 MFEM_SYNC_THREAD;
767
768 for (int step = 0; step < 50; step++)
769 {
770 switch (num_constrained(tmp->flags & FLAG_MASK))
771 {
772 case 0: // findpt_area
773 {
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;
779
780 MFEM_FOREACH_THREAD(j,x,nThreads)
781 {
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);
786 }
787 MFEM_SYNC_THREAD;
788
789 MFEM_FOREACH_THREAD(j,x,nThreads)
790 {
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,
795 wtr+D1D,
796 wtr+2*D1D,
797 wtr+3*D1D,
798 elx[d], qp,
799 D1D);
800 }
801 MFEM_SYNC_THREAD;
802
803 MFEM_FOREACH_THREAD(l,x,2)
804 {
805 resid[l] = tmp->x[l];
806 for (int j = 0; j < D1D; ++j)
807 {
808 resid[l] -= resid_temp[l + j * 2];
809 }
810 }
811 MFEM_FOREACH_THREAD(l,x,4)
812 {
813 jac[l] = 0;
814 for (int j = 0; j < D1D; ++j)
815 {
816 jac[l] += jac_temp[l + j * 4];
817 }
818 }
819 MFEM_SYNC_THREAD;
820
821 MFEM_FOREACH_THREAD(l,x,1)
822 {
823 if (!reject_prior_step_q(fpt, resid, tmp, tol))
824 {
825 newton_area(fpt, jac, resid, tmp, tol);
826 }
827 }
828 MFEM_SYNC_THREAD;
829 break;
830 } //case 0
831 case 1: // findpt_edge
832 {
833 const int ei = edge_index(tmp->flags & FLAG_MASK);
834 const int dn = ei>>1, de = plus_1_mod_2(dn);
835
836 double *wt = r_workspace_ptr;
837 double *resid = wt + 3 * D1D;
838 double *jac = resid + 2; //jac will be row-major
839 double *hess = jac + 2 * 2;
840 findptsElementGEdge_t edge;
841
842 MFEM_FOREACH_THREAD(j,x,D1D)
843 {
844 edge = get_edge(elx, wtend, ei,
845 constraint_workspace,
846 edge_init, j,
847 D1D);
848 }
849 MFEM_SYNC_THREAD;
850
851 // compute basis function info upto 2nd derivative.
852 MFEM_FOREACH_THREAD(j,x,D1D)
853 {
854 if (j == 0) { edge_init = 1u << ei; }
855 lag_eval_second_der(wt, tmp->r[de], j, gll1D,
856 lagcoeff, D1D);
857 }
858 MFEM_SYNC_THREAD;
859
860 MFEM_FOREACH_THREAD(d,x,DIM)
861 {
862 resid[d] = tmp->x[d];
863 jac[2*d] = 0.0;
864 jac[2*d + 1] = 0.0;
865 hess[d] = 0.0;
866 for (int k = 0; k < D1D; ++k)
867 {
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];
872 }
873 }
874 MFEM_SYNC_THREAD;
875
876 // at this point, the Jacobian will be out of
877 // order for edge index 2 and 3 so we need to swap
878 // columns
879 MFEM_FOREACH_THREAD(j,x,1)
880 {
881 if (ei >= 2)
882 {
883 double temp1 = jac[1],
884 temp2 = jac[3];
885 jac[1] = jac[0];
886 jac[3] = jac[2];
887 jac[0] = temp1;
888 jac[2] = temp2;
889 }
890 hess[2] = resid[0]*hess[0] + resid[1]*hess[1];
891 }
892 MFEM_SYNC_THREAD;
893
894 MFEM_FOREACH_THREAD(l,x,1)
895 {
896 // check prior step //
897 if (!reject_prior_step_q(fpt, resid, tmp, tol))
898 {
899 // steep is negative of the gradient of the
900 // objective, so it tells direction of
901 // decrease.
902 double steep = resid[0] * jac[ dn]
903 + resid[1] * jac[2+dn];
904
905 if (steep * tmp->r[dn] < 0)
906 {
907 newton_area(fpt, jac, resid, tmp, tol);
908 }
909 else
910 {
911 newton_edge(fpt, jac, hess[2], resid, de,
912 dn, tmp->flags & FLAG_MASK,
913 tmp, tol);
914 }
915 }
916 }
917 MFEM_SYNC_THREAD;
918 break;
919 }
920 case 2: // findpts_pt
921 {
922 MFEM_FOREACH_THREAD(j,x,1)
923 {
924 int de = 0;
925 int dn = 0;
926 const int pi = point_index(tmp->flags & FLAG_MASK);
927 const findptsElementGPT_t gpt =
928 get_pt(elx, wtend, pi, D1D);
929
930 const double *const pt_x = gpt.x;
931 const double *const jac = gpt.jac;
932 const double *const hes = gpt.hes;
933
934 double resid[DIM], steep[DIM], sr[DIM];
935 for (int d = 0; d < DIM; ++d)
936 {
937 resid[d] = fpt->x[d] - pt_x[d];
938 }
939 steep[0] = jac[0]*resid[0] + jac[2]*resid[1];
940 steep[1] = jac[1]*resid[0] + jac[3]*resid[1];
941
942 sr[0] = steep[0]*tmp->r[0];
943 sr[1] = steep[1]*tmp->r[1];
944
945 if (!reject_prior_step_q(fpt, resid, tmp, tol))
946 {
947 if (sr[0]<0)
948 {
949 if (sr[1]<0)
950 {
951 newton_area(fpt, jac, resid, tmp, tol);
952 }
953 else
954 {
955 de=0;
956 dn=1;
957 const double rh = resid[0]*hes[de]+
958 resid[1]*hes[2+de];
959 newton_edge(fpt, jac, rh, resid, de, dn,
960 tmp->flags &
961 FLAG_MASK &
962 (3u<<(2*dn)),
963 tmp, tol);
964 }
965 }
966 else if (sr[1]<0)
967 {
968 de=1;
969 dn=0;
970 const double rh = resid[0]*hes[de]+
971 resid[1]*hes[2+de];
972 newton_edge(fpt, jac, rh, resid, de, dn,
973 tmp->flags &
974 FLAG_MASK &
975 (3u<<(2*dn)),
976 tmp, tol);
977 }
978 else
979 {
980 fpt->r[0] = tmp->r[0];
981 fpt->r[1] = tmp->r[1];
982 fpt->dist2p = 0;
983 fpt->flags = tmp->flags | CONVERGED_FLAG;
984 }
985 }
986 }
987 MFEM_SYNC_THREAD;
988 break;
989 } //case 3
990 } //switch
991 if (fpt->flags & CONVERGED_FLAG)
992 {
993 break;
994 }
995 MFEM_SYNC_THREAD;
996 MFEM_FOREACH_THREAD(j,x,1)
997 {
998 *tmp = *fpt;
999 }
1000 MFEM_SYNC_THREAD;
1001 } //for int step < 50
1002 } //findpts_el
1003
1004 bool converged_internal = (fpt->flags&FLAG_MASK)==CONVERGED_FLAG;
1005 if (*code_i == CODE_NOT_FOUND || converged_internal ||
1006 fpt->dist2 < *dist2_i)
1007 {
1008 MFEM_FOREACH_THREAD(j,x,1)
1009 {
1010 *el_i = el;
1011 *code_i = converged_internal ? CODE_INTERNAL :
1012 CODE_BORDER;
1013 *dist2_i = fpt->dist2;
1014 }
1015 MFEM_FOREACH_THREAD(j,x,DIM)
1016 {
1017 r_i[j] = fpt->r[j];
1018 }
1019 MFEM_SYNC_THREAD;
1020 if (converged_internal)
1021 {
1022 break;
1023 }
1024 }
1025 } //findpts_local
1026 } //elp
1027 });
1028}
1029
1031 int point_pos_ordering,
1032 Array<unsigned int> &code,
1033 Array<unsigned int> &elem, Vector &ref,
1034 Vector &dist, int npt)
1035{
1036 if (npt == 0)
1037 {
1038 return;
1039 }
1040 auto pp = point_pos.Read();
1041 auto pgslm = gsl_mesh.Read();
1042 auto pwt = DEV.wtend.Read();
1043 auto pbb = DEV.bb.Read();
1044 auto plhm = DEV.lh_min.Read();
1045 auto plhf = DEV.lh_fac.Read();
1046 auto plho = DEV.lh_offset.ReadWrite();
1047 auto pcode = code.Write();
1048 auto pelem = elem.Write();
1049 auto pref = ref.Write();
1050 auto pdist = dist.Write();
1051 auto pgll1d = DEV.gll1d.ReadWrite();
1052 auto plc = DEV.lagcoeff.Read();
1053
1054 switch (DEV.dof1d)
1055 {
1056 case 2:
1057 FindPointsLocal2DKernel<2>(npt, DEV.newt_tol, pp,
1058 point_pos_ordering, pgslm,
1059 NE_split_total, pwt, pbb,
1060 DEV.lh_nx, plhm, plhf, plho,
1061 pcode, pelem, pref, pdist,
1062 pgll1d, plc);
1063 break;
1064 case 3:
1065 FindPointsLocal2DKernel<3>(npt, DEV.newt_tol, pp,
1066 point_pos_ordering, pgslm,
1067 NE_split_total, pwt, pbb,
1068 DEV.lh_nx, plhm, plhf, plho,
1069 pcode, pelem, pref, pdist,
1070 pgll1d, plc);
1071 break;
1072 case 4:
1073 FindPointsLocal2DKernel<4>(npt, DEV.newt_tol, pp,
1074 point_pos_ordering, pgslm,
1075 NE_split_total, pwt, pbb,
1076 DEV.lh_nx, plhm, plhf, plho,
1077 pcode, pelem, pref, pdist,
1078 pgll1d, plc);
1079 break;
1080 case 5:
1081 FindPointsLocal2DKernel<5>(npt, DEV.newt_tol, pp,
1082 point_pos_ordering, pgslm,
1083 NE_split_total, pwt, pbb,
1084 DEV.lh_nx, plhm, plhf, plho,
1085 pcode, pelem, pref, pdist,
1086 pgll1d, plc);
1087 break;
1088 default:
1089 FindPointsLocal2DKernel(npt, DEV.newt_tol, pp,
1090 point_pos_ordering, pgslm,
1091 NE_split_total, pwt, pbb,
1092 DEV.lh_nx, plhm, plhf, plho,
1093 pcode, pelem, pref, pdist,
1094 pgll1d, plc, DEV.dof1d);
1095 break;
1096 }
1097}
1098#undef DIM2
1099#undef DIM
1100#undef CODE_INTERNAL
1101#undef CODE_BORDER
1102#undef CODE_NOT_FOUND
1103#else
1104void FindPointsGSLIB::FindPointsLocal2(const Vector &point_pos,
1105 int point_pos_ordering,
1106 Array<unsigned int> &code,
1107 Array<unsigned int> &elem, Vector &ref,
1108 Vector &dist, int npt) {};
1109#endif
1110} // namespace mfem
1111
1112#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
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.
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
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 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
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