MFEM v4.10.0
Finite element discretization library
Loading...
Searching...
No Matches
gslib.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"
13#include "geom.hpp"
15#include "../general/forall.hpp"
16#include <algorithm> // std::fill
17
18#ifdef MFEM_USE_GSLIB
19
20/* The class GlobalBBoxTensorGridMap, and certain methods including
21dbl_range_diag_expand_2, dbl_range_diag_expand_3, bbox_2_tfm, bbox_3_tfm,
22dbl_range_expand, and obbox{surf/edge}_calc_{D}, were adapted from the gslib
23library. Below is the gslib license and copyright statement:
24
25Copyright (c) 2008-2024, UCHICAGO ARGONNE, LLC.
26
27The UChicago Argonne, LLC as Operator of Argonne National
28Laboratory holds copyright in the Software. The copyright holder
29reserves all rights except those expressly granted to licensees,
30and U.S. Government license rights.
31
32Redistribution and use in source and binary forms, with or without
33modification, are permitted provided that the following conditions
34are met:
35
361. Redistributions of source code must retain the above copyright
37notice, this list of conditions and the disclaimer below.
38
392. Redistributions in binary form must reproduce the above copyright
40notice, this list of conditions and the disclaimer (as noted below)
41in the documentation and/or other materials provided with the
42distribution.
43
443. Neither the name of ANL nor the names of its contributors
45may be used to endorse or promote products derived from this software
46without specific prior written permission.
47
48THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
49"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
50LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
51FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
52UCHICAGO ARGONNE, LLC, THE U.S. DEPARTMENT OF
53ENERGY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
54SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
55TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
56DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
57THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
58(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
59OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
60*/
61
62// Ignore warnings from the gslib header (GCC version)
63#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
64#pragma GCC diagnostic push
65#pragma GCC diagnostic ignored "-Wunused-function"
66#endif
67
68#define CODE_INTERNAL 0
69#define CODE_BORDER 1
70#define CODE_NOT_FOUND 2
71
72// External GSLIB header (the MFEM header is gslib.hpp)
73namespace gslib
74{
75#include "gslib.h"
76#ifndef GSLIB_RELEASE_VERSION //gslib v1.0.7
77#define GSLIB_RELEASE_VERSION 10007
78#endif
79static_assert(std::is_same_v<uint,unsigned int>,
80 "GSLIB's integer-type, 'uint', defined in gslib.h, must be the same as 'unsigned int'!");
81
82extern "C" {
83 struct hash_data_3
84 {
85 ulong hash_n;
86 struct dbl_range bnd[3];
87 double fac[3];
88 uint *offset;
89 };
90
91 struct hash_data_2
92 {
93 ulong hash_n;
94 struct dbl_range bnd[2];
95 double fac[2];
96 uint *offset;
97 };
98
99 struct findpts_dummy_ms_data
100 {
101 unsigned int *nsid;
102 double *distfint;
103 };
104
105 struct findpts_data_3
106 {
107 struct crystal cr;
108 struct findpts_local_data_3 local;
109 struct hash_data_3 hash;
110 struct array savpt;
111 struct findpts_dummy_ms_data fdms;
112 uint fevsetup;
113 };
114
115 struct findpts_data_2
116 {
117 struct crystal cr;
118 struct findpts_local_data_2 local;
119 struct hash_data_2 hash;
120 struct array savpt;
121 struct findpts_dummy_ms_data fdms;
122 uint fevsetup;
123 };
124} //extern C
125
126} //namespace gslib
127
128#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
129#pragma GCC diagnostic pop
130#endif
131
132namespace mfem
133{
134
135// Static helper to map device-side find-points outputs to MFEM reference-space
136// data. nvcc does not allow lambdas in non-public members.
137static void FindPointsDeviceSetCode(int pts_cnt,
138 int ddim,
139 real_t rbtol,
140 real_t bdr_t,
141 unsigned int *d_gsl_code,
142 const real_t *d_gsl_ref,
143 const real_t *d_gsl_dist,
144 const unsigned int *d_gsl_elem,
145 real_t *d_gsl_mfem_ref,
146 unsigned int *d_gsl_mfem_elem)
147{
148 mfem::forall(pts_cnt, [=] MFEM_HOST_DEVICE (int index)
149 {
150 if (d_gsl_code[index] == CODE_NOT_FOUND)
151 {
152 return;
153 }
154 d_gsl_mfem_elem[index] = d_gsl_elem[index];
155
156 bool internal = true;
157 for (int d = 0; d < ddim; d++)
158 {
159 const real_t r_val = d_gsl_ref[index * ddim + d];
160 const real_t val = 0.5 * (r_val + 1.0);
161 d_gsl_mfem_ref[index * ddim + d] = val;
162 if (val < rbtol || val > 1.0 - rbtol)
163 {
164 internal = false;
165 }
166 }
167
168 const int setcode = internal ? CODE_INTERNAL : CODE_BORDER;
169 d_gsl_code[index] = (setcode == CODE_BORDER &&
170 d_gsl_dist[index] > bdr_t)
171 ? CODE_NOT_FOUND : setcode;
172 });
173}
174
175// Static helper: scatter interpolated values back into field_out.
176// nvcc does not allow lambdas in non-public members.
177static void InterpolateDeviceScatter(int nlocal,
178 const int *d_index_temp,
179 const real_t *d_interp_vals,
180 real_t *d_field_out,
181 int interp_offset,
182 int ncomp,
183 int pts_cnt,
184 int ordering)
185{
186 if (nlocal == 0 || ncomp == 0) { return; }
187
188 mfem::forall(nlocal*ncomp, [=] MFEM_HOST_DEVICE (int k)
189 {
190 const int j = k % nlocal;
191 const int i = k / nlocal;
192 const int pt_index = d_index_temp[j];
193 const int idx = (ordering == Ordering::byNODES) ?
194 pt_index + i*pts_cnt :
195 pt_index*ncomp + i;
196 d_field_out[idx] = d_interp_vals[j + interp_offset*i];
197 });
198}
199
201 : mesh(NULL),
202 fec_map_lin(NULL),
203 fdataD(NULL), cr(NULL), gsl_comm(NULL),
204 dim(-1), spacedim(-1), points_cnt(-1), setupflag(false),
205 default_interp_value(0),
206 avgtype(AvgType::ARITHMETIC), bdr_tol(1e-8)
207{
208 mesh_split.SetSize(4);
209 ir_split.SetSize(4);
210 ir_split_sol.SetSize(4);
211 fes_rst_map.SetSize(4);
212 gf_rst_map.SetSize(4);
213 for (int i = 0; i < mesh_split.Size(); i++)
214 {
215 mesh_split[i] = nullptr;
216 ir_split[i] = nullptr;
217 ir_split_sol[i] = nullptr;
218 fes_rst_map[i] = nullptr;
219 gf_rst_map[i] = nullptr;
220 }
221
222 gsl_comm = new gslib::comm;
223#ifdef MFEM_USE_MPI
224 if (!Mpi::IsInitialized()) { Mpi::Init(); }
225 MPI_Comm comm = MPI_COMM_WORLD;
226 comm_init(gsl_comm, comm);
227#else
228 comm_init(gsl_comm, 0);
229#endif
230}
231
233 const double bbox_rel_size_inc,
234 const double newt_tol, const int npt_max)
236{
237 Setup(mesh_in, bbox_rel_size_inc, newt_tol, npt_max);
238}
239
241{
242 FreeData();
243#ifdef MFEM_USE_MPI
244 if (!Mpi::IsFinalized()) // currently segfaults inside gslib otherwise
245#endif
246 {
247 comm_free(gsl_comm);
248 delete gsl_comm;
249 }
250 for (int i = 0; i < mesh_split.Size(); i++)
251 {
252 if (mesh_split[i]) { delete mesh_split[i]; mesh_split[i] = nullptr; }
253 if (ir_split[i]) { delete ir_split[i]; ir_split[i] = nullptr; }
254 if (ir_split_sol[i]) { delete ir_split_sol[i]; ir_split_sol[i] = nullptr; }
255 if (fes_rst_map[i]) { delete fes_rst_map[i]; fes_rst_map[i] = nullptr; }
256 if (gf_rst_map[i]) { delete gf_rst_map[i]; gf_rst_map[i] = nullptr; }
257 }
258 if (fec_map_lin) { delete fec_map_lin; fec_map_lin = nullptr; }
259}
260
261#ifdef MFEM_USE_MPI
263 : mesh(NULL),
264 fec_map_lin(NULL),
265 fdataD(NULL), cr(NULL), gsl_comm(NULL),
266 dim(-1), spacedim(-1), points_cnt(-1), setupflag(false),
267 default_interp_value(0),
268 avgtype(AvgType::ARITHMETIC), bdr_tol(1e-8)
269{
270 mesh_split.SetSize(4);
271 ir_split.SetSize(4);
272 ir_split_sol.SetSize(4);
273 fes_rst_map.SetSize(4);
274 gf_rst_map.SetSize(4);
275 for (int i = 0; i < mesh_split.Size(); i++)
276 {
277 mesh_split[i] = nullptr;
278 ir_split[i] = nullptr;
279 ir_split_sol[i] = nullptr;
280 fes_rst_map[i] = nullptr;
281 gf_rst_map[i] = nullptr;
282 }
283
284 gsl_comm = new gslib::comm;
285 comm_init(gsl_comm, comm_);
286}
287
289 const double bbox_rel_size_inc,
290 const double newt_tol, const int npt_max)
291 : FindPointsGSLIB(mesh_in.GetComm())
292{
293 Setup(mesh_in, bbox_rel_size_inc, newt_tol, npt_max);
294}
295#endif
296
298{
299 if (cr == NULL)
300 {
301 cr = new gslib::crystal;
302 crystal_init(cr, gsl_comm);
303 }
304}
305
307{
308 if (cr == NULL) { return; }
309
310#ifdef MFEM_USE_MPI
311 if (!Mpi::IsFinalized()) // currently segfaults inside gslib otherwise
312#endif
313 {
314 crystal_free(cr);
315 }
316 delete cr;
317 cr = NULL;
318 DEV.cr = NULL;
319}
320
321void FindPointsGSLIB::Setup(Mesh &m, const double bbox_rel_size_inc,
322 const double newt_tol,
323 const int npt_max)
324{
325 MFEM_VERIFY(m.GetNodes() != nullptr, "Mesh nodes are required.");
326 const int meshOrder = m.GetNodes()->FESpace()->GetMaxElementOrder();
327
328 // call FreeData if FindPointsGSLIB::Setup has been called already
329 if (m.Dimension() != m.SpaceDimension())
330 {
331 SetupSurf(m, bbox_rel_size_inc, newt_tol);
332 return;
333 }
334 if (setupflag) { FreeData(); }
335
336 SetupCrystal();
337
338 mesh = &m;
339 dim = mesh->Dimension();
340 spacedim = dim;
341 const unsigned dof1D = meshOrder + 1;
342
344
346
348
350 DEV.dof1d = (int)dof1D;
351 if (dim == 2)
352 {
353 unsigned nr[2] = { dof1D, dof1D };
354 unsigned mr[2] = { 2*dof1D, 2*dof1D };
355 double * const elx[2] =
356 {
357 mesh_points_cnt == 0 ? nullptr : &gsl_mesh(0),
358 mesh_points_cnt == 0 ? nullptr : &gsl_mesh(mesh_points_cnt)
359 };
360 fdataD = findpts_setup_2(gsl_comm, elx, nr, NE_split_total, mr,
361 bbox_rel_size_inc, DEV.local_hash_size,
362 mesh_points_cnt, npt_max, newt_tol);
363 }
364 else
365 {
366 unsigned nr[3] = { dof1D, dof1D, dof1D };
367 unsigned mr[3] = { 2*dof1D, 2*dof1D, 2*dof1D };
368 double * const elx[3] =
369 {
370 mesh_points_cnt == 0 ? nullptr : &gsl_mesh(0),
371 mesh_points_cnt == 0 ? nullptr : &gsl_mesh(mesh_points_cnt),
372 mesh_points_cnt == 0 ? nullptr : &gsl_mesh(2*mesh_points_cnt)
373 };
374 fdataD = findpts_setup_3(gsl_comm, elx, nr, NE_split_total, mr,
375 bbox_rel_size_inc, DEV.local_hash_size,
376 mesh_points_cnt, npt_max, newt_tol);
377 }
378 setupflag = true;
379}
380
381
382/* Calculates the diagonal length of the bounding box and expands its bounds by
383 * 0.5*len*tol at both its min and max values.
384 * Returns the length of the diagonal (could be used for expanding obboxes).
385 */
386double dbl_range_diag_expand_2(struct gslib::dbl_range *b, double tol)
387{
388 double l[2] = { b[0].max-b[0].min, b[1].max-b[1].min };
389 double len = sqrt(l[0]*l[0] + l[1]*l[1])*0.5*tol;
390 for (int i=0; i<2; i++)
391 {
392 b[i].min = b[i].min - len;
393 b[i].max = b[i].max + len;
394 }
395 return len;
396}
397
398double dbl_range_diag_expand_3(struct gslib::dbl_range *b, double tol)
399{
400 double l[3] = { b[0].max-b[0].min, b[1].max-b[1].min, b[2].max-b[2].min };
401 double len = sqrt(l[0]*l[0] + l[1]*l[1] + l[2]*l[2])*0.5*tol;
402 for (int i=0; i<3; i++)
403 {
404 b[i].min = b[i].min - len;
405 b[i].max = b[i].max + len;
406 }
407 return len;
408}
409
410static void bbox_2_tfm(double *tfm, const double x0[2], const double Ji[4],
411 const double *x, const double *y, unsigned n)
412{
413 unsigned i;
414 for (i=0; i<n; ++i)
415 {
416 const double dx = x[i]-x0[0], dy = y[i]-x0[1];
417 tfm[ i] = Ji[0]*dx + Ji[1]*dy;
418 tfm[n+i] = Ji[2]*dx + Ji[3]*dy;
419 }
420}
421
422static void bbox_3_tfm(double *tfm, const double x0[3], const double Ji[9],
423 const double *x, const double *y, const double *z,
424 unsigned n)
425{
426 unsigned i;
427 for (i=0; i<n; ++i)
428 {
429 const double dx = x[i]-x0[0], dy = y[i]-x0[1], dz = z[i]-x0[2];
430 tfm[ i] = Ji[0]*dx + Ji[1]*dy + Ji[2]*dz;
431 tfm[ n+i] = Ji[3]*dx + Ji[4]*dy + Ji[5]*dz;
432 tfm[2*n+i] = Ji[6]*dx + Ji[7]*dy + Ji[8]*dz;
433 }
434}
435
436static struct gslib::dbl_range dbl_range_expand(struct gslib::dbl_range b,
437 double tol)
438{
439 double a = (b.min+b.max)/2, l = (b.max-b.min)*(1+tol)/2;
440 struct gslib::dbl_range m;
441 m.min = a-l, m.max = a+l;
442 return m;
443}
444
445static MFEM_HOST_DEVICE inline void MapSplitTriangleQuadToTriangle(
446 const int tri_id, const double u, const double v, double &tx, double &ty)
447{
448 const double N0 = (1.0-u)*(1.0-v);
449 const double N1 = u*(1.0-v);
450 const double N2 = u*v;
451 const double N3 = (1.0-u)*v;
452
453 // Must match the quad -> triangle split in SetupSplitMeshes.
454 const double vx[3][4] =
455 {
456 {0.0, 0.5, 1.0/3.0, 0.0 },
457 {0.5, 1.0, 0.5, 1.0/3.0},
458 {0.0, 1.0/3.0, 0.5, 0.0 }
459 };
460 const double vy[3][4] =
461 {
462 {0.0, 0.0, 1.0/3.0, 0.5 },
463 {0.0, 0.0, 0.5, 1.0/3.0},
464 {0.5, 1.0/3.0, 0.5, 1.0 }
465 };
466
467 tx = N0*vx[tri_id][0] + N1*vx[tri_id][1]
468 + N2*vx[tri_id][2] + N3*vx[tri_id][3];
469 ty = N0*vy[tri_id][0] + N1*vy[tri_id][1]
470 + N2*vy[tri_id][2] + N3*vy[tri_id][3];
471}
472
473static void VerifyAABBPadLayout(const Vector *aabb_sz_inc, const uint nel,
474 const int sd)
475{
476 if (!aabb_sz_inc) { return; }
477
478 const int sz = aabb_sz_inc->Size();
479 MFEM_VERIFY(sz == 1 || sz == (int)nel || sz == sd || sz == (int)nel*sd,
480 "Invalid aabb_sz_inc length for SetupSurfWithAABBExpansion: "
481 "expected 1, NE, SpaceDim, or NE*SpaceDim.");
482}
483
484static double GetAABBPad(const Vector *aabb_sz_inc, const int aabb_sz_inc_size,
485 const uint nel, const int sd, const uint e,
486 const int d)
487{
488 if (!aabb_sz_inc) { return 0.0; }
489
490 MFEM_ASSERT(aabb_sz_inc_size == 1 || aabb_sz_inc_size == (int)nel ||
491 aabb_sz_inc_size == sd || aabb_sz_inc_size == (int)nel*sd,
492 "Invalid aabb_sz_inc layout.");
493
494 double s = 0.0;
495 if (aabb_sz_inc_size == 1) { s = (*aabb_sz_inc)(0); }
496 else if (aabb_sz_inc_size == (int)nel) { s = (*aabb_sz_inc)((int)e); }
497 else if (aabb_sz_inc_size == sd) { s = (*aabb_sz_inc)(d); }
498 else { s = (*aabb_sz_inc)((int)e*sd + d); }
499
500 MFEM_VERIFY(s >= 0.0,
501 "aabb_sz_inc absolute AABB expansion must be non-negative.");
502 return 0.5*s;
503}
504
506 const double *const elx[3],
507 const unsigned n,
508 uint nel,
509 const unsigned m,
510 const double tol,
511 const bool store_obb)
512{
513 auto h_bb = bb.HostWrite();
514 const double *x = elx[0], *y = elx[1], *z = elx[2];
515 const int n_el_ents = store_obb ? 18 : 6;
516 // 3(c0) + 3(aabb_min) + 3(aabb_max) + 9(A)
517
518 const unsigned n2 = n*n;
519 const unsigned lbsize = gslib::lob_bnd_size(n, m);
520
521 // 2*n for tensor_ig2
522 // 2*m*(n+m+1) for lob_bnd_2 (always greater than 2*n for n and m >=1)
523 // gslib::gll_lag_size(n) for lag_setup and then lag
524 unsigned wsize = std::max(2*m*(n+m+1), store_obb?gslib::gll_lag_size(n):0u);
525
526 Vector datavec(lbsize + wsize + (store_obb ? 2*n : 0));
527 double *data = datavec.GetData();
528
529 double *const I0 = store_obb ? data : nullptr; // basis and derivative at r=0
530 double *const lob_bnd_data = store_obb ? (data + 2*n) : data;
531 double *const work = lob_bnd_data + lbsize;
532
533 gslib::lob_bnd_setup(lob_bnd_data, n, m); // setup machinery for bounding
534
535 if (!store_obb)
536 {
537 for (int ie = 0; ie < (int)nel; ie++, x += n2, y += n2, z += n2)
538 {
539 struct gslib::dbl_range ab[3];
540 ab[0] = gslib::lob_bnd_2(lob_bnd_data,n,m, lob_bnd_data,n,m, x, work);
541 ab[1] = gslib::lob_bnd_2(lob_bnd_data,n,m, lob_bnd_data,n,m, y, work);
542 ab[2] = gslib::lob_bnd_2(lob_bnd_data,n,m, lob_bnd_data,n,m, z, work);
543
545
546 h_bb[n_el_ents*ie + 0] = ab[0].min;
547 h_bb[n_el_ents*ie + 1] = ab[1].min;
548 h_bb[n_el_ents*ie + 2] = ab[2].min;
549 h_bb[n_el_ents*ie + 3] = ab[0].max;
550 h_bb[n_el_ents*ie + 4] = ab[1].max;
551 h_bb[n_el_ents*ie + 5] = ab[2].max;
552 }
553 return;
554 }
555
556 gslib::lagrange_fun *const lag = gslib::gll_lag_setup(work, n);
557 lag(I0, work, n, 1, 0);
559 for (int ie = 0; (unsigned)ie < nel; ie++,x+=n2,y+=n2,z+=n2)
560 {
561 struct gslib::dbl_range ab[3];
562 struct gslib::dbl_range tb[3];
563 double x0[3], tv[9], A[9];
564
565 /*
566 * Find the center of the element (r=0 ref. coord.) in physical space
567 * and store in x0.
568 * tv[0], tv[1], tv[2]: kept empty at this point for convenience.
569 * tv[3], tv[4]: dx/dr, dx/ds
570 * tv[5], tv[6]: dy/dr, dy/ds
571 * tv[7], tv[8]: dz/dr, dz/ds
572 */
573 x0[0] = gslib::tensor_ig2(tv+3, I0,n, I0,n, x, work);
574 x0[1] = gslib::tensor_ig2(tv+5, I0,n, I0,n, y, work);
575 x0[2] = gslib::tensor_ig2(tv+7, I0,n, I0,n, z, work);
576
577 // tangent vector 1 moved to tv[0], tv[1], tv[2]
578 tv[0] = tv[3], tv[1] = tv[5], tv[2] = tv[7];
579 // tangent vector 2 moved to tv[3], tv[4], tv[5]
580 tv[3] = tv[4], tv[4] = tv[6], tv[5] = tv[8];
581 // normal vector to the plane formed by t1 and t2 (cross product)
582 // is stored in tv[6], tv[7], tv[8]
583 tv[6] = tv[1]*tv[5] - tv[2]*tv[4];
584 tv[7] = tv[2]*tv[3] - tv[0]*tv[5];
585 tv[8] = tv[0]*tv[4] - tv[1]*tv[3];
586 // normalize the normal vector
587 const double nmag = sqrt(tv[6]*tv[6] + tv[7]*tv[7] + tv[8]*tv[8]);
588 tv[6] = tv[6]/nmag;
589 tv[7] = tv[7]/nmag;
590 tv[8] = tv[8]/nmag;
591
592 // Rodrigues formula to compute the rotation matrix
593 // Axis of rotation is n x [0,0,1] = [n_2, -n_1, 0], and we must
594 // normalize it
595 double nmag2 = tv[6]*tv[6] + tv[7]*tv[7];
596 if (nmag2 > 0)
597 {
598 nmag2 = sqrt(nmag2);
599 tv[7] = tv[7]/nmag2;
600 tv[6] = tv[6]/nmag2;
601 }
602 double kx = tv[7];
603 double ky = -tv[6];
604 double kz = 0.0;
605
606 double ct = tv[8];
607 double st = nmag2; //1.0 - ct*ct;
608
609 // row-major
610 A[0] = 1.0 + st*0.0 + (1.0-ct)*(-ky*ky-kz*kz);
611 A[1] = 0.0 + st*(0.0) + (1.0-ct)*(kx*ky);
612 A[2] = 0.0 + st*(ky) + (1.0-ct)*(kx*kz);
613
614 A[3] = 0.0 + st*(0.0) + (1.0-ct)*(kx*ky);
615 A[4] = 1.0 + st*(0.0) + (1.0-ct)*(-kx*kx-kz*kz);
616 A[5] = 0.0 + st*(-kx) + (1.0-ct)*(ky*kz);
617
618 A[6] = 0.0 + st*(-ky) + (1.0-ct)*(kx*kz);
619 A[7] = 0.0 + st*(kx) + (1.0-ct)*(ky*kz);
620 A[8] = 1.0 + st*(0.0) + (1.0-ct)*(-kx*kx-ky*ky);
621
622 // compute bounds in physical space
623 ab[0] = gslib::lob_bnd_2(lob_bnd_data,n,m, lob_bnd_data,n,m, x, work);
624 ab[1] = gslib::lob_bnd_2(lob_bnd_data,n,m, lob_bnd_data,n,m, y, work);
625 ab[2] = gslib::lob_bnd_2(lob_bnd_data,n,m, lob_bnd_data,n,m, z, work);
626 // expand bounding box based on (tol*diagonal_length) in each direction
627 // to avoid 0 extent in 1 direction.
628 double aabb_diag_len = dbl_range_diag_expand_3(ab, tol);
629 h_bb[n_el_ents*ie + 3] = ab[0].min;
630 h_bb[n_el_ents*ie + 4] = ab[1].min;
631 h_bb[n_el_ents*ie + 5] = ab[2].min;
632 h_bb[n_el_ents*ie + 6] = ab[0].max;
633 h_bb[n_el_ents*ie + 7] = ab[1].max;
634 h_bb[n_el_ents*ie + 8] = ab[2].max;
635
636 // rotate to align normal with z-axis
637 Array<double> xtfm(3*n2);
638 bbox_3_tfm(xtfm.GetData(), x0,A, x,y,z,n2);
639 // The rotated z-coords are used to calculate z-bounds.
640 tb[2] = gslib::lob_bnd_2(lob_bnd_data,n,m, lob_bnd_data,n,m,
641 xtfm.GetData()+2*n2, work);
642
643 tb[2].min -= aabb_diag_len;
644 tb[2].max += aabb_diag_len;
645
646 // Also apply A to the tangent vectors, which allows us to
647 // calculate the Jacobian matrix at the rotated element center.
648 // NOTE that the z components of the rotated tangent vectors will
649 // become zero, since the normal vector is aligned with the z-axis.
650 double J[4], Ji[4];
651 J[0] = A[0]*tv[0] + A[1]*tv[1] + A[2]*tv[2]; // rotated dx/dr
652 J[1] = A[0]*tv[3] + A[1]*tv[4] + A[2]*tv[5]; // rotated dx/ds
653 J[2] = A[3]*tv[0] + A[4]*tv[1] + A[5]*tv[2]; // rotated dy/dr
654 J[3] = A[3]*tv[3] + A[4]*tv[4] + A[5]*tv[5]; // rotated dy/ds
655 DenseMatrix JM(J, 2, 2);
656 DenseMatrix JiM(Ji, 2, 2);
657 CalcInverse(JM, JiM);
658
659 // Now transform the already rotated x,y coordinates according to Ji to
660 // their reference space.
661 // Important to note that the nodes used here already have the element
662 // center at (0,0). Hence, Ji can be directly applied to them.
663 for (unsigned i=0; i<n2; ++i)
664 {
665 const double xt = xtfm[i], yt = xtfm[n2+i];
666 xtfm[ i] = Ji[0]*xt + Ji[1]*yt;
667 xtfm[n2+i] = Ji[2]*xt + Ji[3]*yt;
668 }
669 // Bound these reference space xy coordinates
670 tb[0] = gslib::lob_bnd_2(lob_bnd_data,n,m, lob_bnd_data,n,m,
671 xtfm.GetData(), work);
672 tb[1] = gslib::lob_bnd_2(lob_bnd_data,n,m, lob_bnd_data,n,m,
673 xtfm.GetData()+n2, work);
674 // Expand the bounds based on the tol
675 tb[0] = dbl_range_expand(tb[0],tol);
676 tb[1] = dbl_range_expand(tb[1],tol);
677
678 /* We now have a BB whose bounds represent bounds of a OBB around the
679 * original element.
680 *
681 * We calculate the center of the OBB in physical space by calculating
682 * the center of this BB, which is the same as the displacement needed
683 * to move from the element center in the transformed space to the BB center. This displacement is then untransformed by applying (Ji.A)^-1
684 * to it, and added to known physical element center.
685 *
686 * This BB does not necessarily have known fixed size like [-1,1].
687 * So, we premultiply a length scaling matrix, say L, to Ji.A to
688 * L.Ji.A. This is the total transformation needed to move a physical
689 * location that is inside the physical OBB to a location within [-1,1]^3. Any transformed point not in [-1,1]^3 is outside the OBB.
690 *
691 * It must be noted: this transformation matrix is only applied to
692 * points that have been translated by the physical OBB center.
693 */
694 {
695 // The center of the BB in the transformed space
696 const double av0 = (tb[0].min+tb[0].max)/2,
697 av1 = (tb[1].min+tb[1].max)/2,
698 av2 = (tb[2].min+tb[2].max)/2;
699 // First untransform the x,y coordinates by J to obtain all
700 // components in the rotated space
701 const double Jav0 = J[0]*av0 + J[1]*av1,
702 Jav1 = J[2]*av0 + J[3]*av1;
703 // The physical displacement needed to move from the element center
704 // to the OBB center is calculated by "un"rotating {Jav0,Jav1,av2}
705 // by applying inverse of A.
706 // The physical untransformed OBB center can then be obtained.
707
708 h_bb[n_el_ents*ie + 0] = x0[0] + A[0]*Jav0 + A[3]*Jav1 + A[6]*av2;
709 h_bb[n_el_ents*ie + 1] = x0[1] + A[1]*Jav0 + A[4]*Jav1 + A[7]*av2;
710 h_bb[n_el_ents*ie + 2] = x0[2] + A[2]*Jav0 + A[5]*Jav1 + A[8]*av2;
711 }
712
713 // Finally, obtain (L.Ji.A) and store it in out->A
714 {
715 // The scaling matrix L's diagonal terms, needed to scale the
716 // transformation to [-1,1]^3.
717 const double di0 = 2/(tb[0].max-tb[0].min),
718 di1 = 2/(tb[1].max-tb[1].min),
719 di2 = 2/(tb[2].max-tb[2].min);
720
721 // We finally construct the final transformation matrix A=L.Ji.A.
722 // This maps a position relative to OBB center to a position in
723 // [-1,1]^3, if the position is inside the OBB.
724 h_bb[n_el_ents*ie + 9 ]=di0*(Ji[0]*A[0] + Ji[1]*A[3]);
725 h_bb[n_el_ents*ie + 10]=di0*(Ji[0]*A[1] + Ji[1]*A[4]);
726 h_bb[n_el_ents*ie + 11]=di0*(Ji[0]*A[2] + Ji[1]*A[5]);
727 h_bb[n_el_ents*ie + 12]=di1*(Ji[2]*A[0] + Ji[3]*A[3]);
728 h_bb[n_el_ents*ie + 13]=di1*(Ji[2]*A[1] + Ji[3]*A[4]);
729 h_bb[n_el_ents*ie + 14]=di1*(Ji[2]*A[2] + Ji[3]*A[5]);
730 h_bb[n_el_ents*ie + 15]=di2*A[6];
731 h_bb[n_el_ents*ie + 16]=di2*A[7];
732 h_bb[n_el_ents*ie + 17]=di2*A[8];
733 }
734 }
735}
736
738 const double *const elx[2],
739 const unsigned nr,
740 uint nel,
741 const unsigned mr,
742 const double tol,
743 const bool store_obb)
744{
745 auto h_bb = bb.HostWrite();
746 const double *x = elx[0];
747 const double *y = elx[1];
748
749 const int n_el_ents = store_obb ? 10 : 4; // store only AABB if !store_obb
750
751 const unsigned lbsize0 = gslib::lob_bnd_size(nr,mr);
752 unsigned wsize = std::max(2*nr+2*mr,store_obb ? gslib::gll_lag_size(nr) : 0);
753 Vector datavec(lbsize0 + wsize + (store_obb ? 2*nr : 0));
754 double *data = datavec.GetData();
755
756 double *const I0r = store_obb ? data : nullptr;
757 double *const lob_bnd_data_r = store_obb ? data + 2*nr : data;
758 double *const work = lob_bnd_data_r + lbsize0;
759
760 gslib::lob_bnd_setup(lob_bnd_data_r, nr, mr);
761
762 if (!store_obb)
763 {
764 for (int ie = 0; ie < (int)nel; ie++, x+=nr, y+=nr)
765 {
766 struct gslib::dbl_range ab[2];
767 ab[0] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, x, work);
768 ab[1] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, y, work);
769
771
772 h_bb[n_el_ents*ie + 0] = ab[0].min;
773 h_bb[n_el_ents*ie + 1] = ab[1].min;
774 h_bb[n_el_ents*ie + 2] = ab[0].max;
775 h_bb[n_el_ents*ie + 3] = ab[1].max;
776 }
777 return;
778 }
779
780 gslib::lagrange_fun *const lag = gslib::gll_lag_setup(work, nr);
781 lag(I0r, work, nr,1, 0);
782
783 for (int ie = 0; (unsigned)ie < nel; ie++,x+=nr,y+=nr)
784 {
785 double x0[2], A[4];
786 struct gslib::dbl_range ab[2], tb[2];
787
788 // Find the physical-space coordinates of center of the element (r=0).
789 // A holds the Jacobian/tangent dx/dr and dy/dr
790 x0[0] = gslib::tensor_ig1(A,I0r,nr,x);
791 x0[1] = gslib::tensor_ig1(A+1,I0r,nr,y);
792 // Normalize the tangent and construct the 2x2 rotation matrix A that
793 // aligns the tangent with the x-axis. Rows of A are [tangent, normal].
794 A[2] = sqrt(A[0]*A[0] + A[1]*A[1]);
795 A[0] = A[0]/A[2];
796 A[1] = A[1]/A[2];
797 A[2] = -A[1];
798 A[3] = A[0];
799
800 // Compute AABB in x and y.
801 ab[0] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, x, work);
802 ab[1] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, y, work);
803 // Transform nodes to the reference frame and compute bounds.
804 bbox_2_tfm(work, x0, A, x, y, nr);
805 tb[0] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, work, work+2*nr);
806 tb[1] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, work+nr, work+2*nr);
807
808 // expand AABB
809 double aabb_diag_len = dbl_range_diag_expand_2(ab, tol);
810
811 // The center of the OBB in the rotated frame.
812 const double av0 = (tb[0].min+tb[0].max)/2,
813 av1 = (tb[1].min+tb[1].max)/2;
814 // A is orthogonal, so A^-1 = A^T. Untransform [av0,av1] back to
815 // physical space to get the physical OBB center.
816 const double dx0 = A[0]*av0 - A[1]*av1,
817 dx1 = -A[2]*av0 + A[3]*av1;
818 h_bb[n_el_ents*ie + 0] = x0[0] + dx0;
819 h_bb[n_el_ents*ie + 1] = x0[1] + dx1;
820 h_bb[n_el_ents*ie + 2] = ab[0].min;
821 h_bb[n_el_ents*ie + 3] = ab[1].min;
822 h_bb[n_el_ents*ie + 4] = ab[0].max;
823 h_bb[n_el_ents*ie + 5] = ab[1].max;
824
825 // Expand the rotated-frame bounds by aabb_diag_len, then premultiply
826 // A by the scaling matrix L = diag(di0,di1) to obtain L*A. This maps
827 // a physical displacement from the OBB center to [-1,1]^2.
828 tb[0].min -= aabb_diag_len;
829 tb[0].max += aabb_diag_len;
830 tb[1].min -= aabb_diag_len;
831 tb[1].max += aabb_diag_len;
832 const double di0 = 2/(tb[0].max-tb[0].min),
833 di1 = 2/(tb[1].max-tb[1].min);
834 h_bb[n_el_ents*ie + 6]=di0*A[0];
835 h_bb[n_el_ents*ie + 7]=di0*A[1];
836 h_bb[n_el_ents*ie + 8]=di1*A[2];
837 h_bb[n_el_ents*ie + 9]=di1*A[3];
838 }
839}
840
842 const double *const elx[3],
843 const unsigned nr,
844 uint nel,
845 const unsigned mr,
846 const double tol,
847 const bool store_obb)
848{
849 auto h_bb = bb.HostWrite();
850 const double *x = elx[0];
851 const double *y = elx[1];
852 const double *z = elx[2];
853 const int n_el_ents = store_obb ? 18 : 6;
854
855 const unsigned lbsize0 = gslib::lob_bnd_size(nr,mr);
856 // 2*mr for lob_bnd_1
857 // +3*nr for storing rotated coordinates computed by bbox_3_tfm
858 const unsigned wsize = store_obb ?
859 std::max(3*nr+2*mr, gslib::gll_lag_size(nr)) :
860 (2*nr + 2*mr);
861
862 Vector datavec(lbsize0 + wsize + (store_obb ? 2*nr : 0));
863 double *data = datavec.GetData();
864
865 double *const I0r = store_obb ? data : nullptr;
866 double *const lob_bnd_data_r = store_obb ? data + 2*nr : data;
867 double *const work = lob_bnd_data_r + lbsize0;
868
869 gslib::lob_bnd_setup(lob_bnd_data_r, nr, mr);
870
871 if (!store_obb)
872 {
873 for (int ie = 0; ie < (int)nel; ie++, x += nr, y += nr, z += nr)
874 {
875 struct gslib::dbl_range ab[3];
876 ab[0] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, x, work);
877 ab[1] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, y, work);
878 ab[2] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, z, work);
879
881
882 h_bb[n_el_ents*ie + 0] = ab[0].min;
883 h_bb[n_el_ents*ie + 1] = ab[1].min;
884 h_bb[n_el_ents*ie + 2] = ab[2].min;
885 h_bb[n_el_ents*ie + 3] = ab[0].max;
886 h_bb[n_el_ents*ie + 4] = ab[1].max;
887 h_bb[n_el_ents*ie + 5] = ab[2].max;
888 }
889 return;
890 }
891
892 gslib::lagrange_fun *const lag = gslib::gll_lag_setup(work, nr);
893 lag(I0r, work, nr, 1, 0);
894
895 for (int ie = 0; (unsigned)ie < nel; ie++,x+=nr,y+=nr,z+=nr)
896 {
897 double x0[3], A[9], Ai[9];
898 struct gslib::dbl_range ab[3], tb[3];
899
900 // Find the physical-space coordinates of center of the element (r=0).
901 // A holds the Jacobian/tangent dx/dr, dy/dr, and dz/dr.
902 x0[0] = gslib::tensor_ig1(A,I0r,nr,x);
903 x0[1] = gslib::tensor_ig1(A+1,I0r,nr,y);
904 x0[2] = gslib::tensor_ig1(A+2,I0r,nr,z);
905
906 // Normalize the tangent vector.
907 double nmag = A[0]*A[0] + A[1]*A[1] + A[2]*A[2];
908 if (nmag > 0)
909 {
910 nmag = sqrt(nmag);
911 A[0] = A[0]/nmag;
912 A[1] = A[1]/nmag;
913 A[2] = A[2]/nmag;
914 }
915
916 double nmag2 = A[0]*A[0] + A[1]*A[1];
917 if (nmag2 > 0)
918 {
919 nmag2 = sqrt(nmag2);
920 A[1] = A[1]/nmag2;
921 A[0] = A[0]/nmag2;
922 }
923 // Rodrigues formula: axis of rotation is tangent x [1,0,0],
924 // normalized to k = [kx,ky,kz]. ct and st are cos/sin of the angle.
925 double kx = A[1];
926 double ky = -A[0];
927 double kz = 0.0;
928
929 double ct = A[2];
930 double st = nmag2; //1.0 - ct*ct;
931
932 // Construct the 3x3 rotation matrix A that aligns the tangent with
933 // the x-axis (row-major).
934 A[0] = 1.0 + st*0.0 + (1.0-ct)*(-ky*ky-kz*kz);
935 A[1] = 0.0 + st*(0.0) + (1.0-ct)*(kx*ky);
936 A[2] = 0.0 + st*(ky) + (1.0-ct)*(kx*kz);
937
938 A[3] = 0.0 + st*(0.0) + (1.0-ct)*(kx*ky);
939 A[4] = 1.0 + st*(0.0) + (1.0-ct)*(-kx*kx-kz*kz);
940 A[5] = 0.0 + st*(-kx) + (1.0-ct)*(ky*kz);
941
942 A[6] = 0.0 + st*(-ky) + (1.0-ct)*(kx*kz);
943 A[7] = 0.0 + st*(kx) + (1.0-ct)*(ky*kz);
944 A[8] = 1.0 + st*(0.0) + (1.0-ct)*(-kx*kx-ky*ky);
945
946 DenseMatrix AM(A, 3, 3);
947 DenseMatrix AiM(Ai, 3, 3);
948 CalcInverse(AM, AiM);
949
950 // Compute AABB in x, y, and z.
951 ab[0] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, x, work);
952 ab[1] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, y, work);
953 ab[2] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, z, work);
954 // Transform nodes to the rotated frame and compute bounds.
955 bbox_3_tfm(work, x0, A, x, y, z, nr);
956 tb[0] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, work, work+3*nr);
957 tb[1] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, work+nr, work+3*nr);
958 tb[2] = gslib::lob_bnd_1(lob_bnd_data_r, nr, mr, work+2*nr, work+3*nr);
959
960 // expand AABB
961 double aabb_diag_len = dbl_range_diag_expand_3(ab, tol);
962
963 // The center of the OBB in the rotated frame.
964 const double av0 = (tb[0].min+tb[0].max)/2,
965 av1 = (tb[1].min+tb[1].max)/2,
966 av2 = (tb[2].min+tb[2].max)/2;
967 // Untransform [av0,av1,av2] back to physical space using A^-1
968 // to obtain the physical OBB center.
969 h_bb[n_el_ents*ie + 0] = x0[0] + Ai[0]*av0 + Ai[1]*av1 + Ai[2]*av2;
970 h_bb[n_el_ents*ie + 1] = x0[1] + Ai[3]*av0 + Ai[4]*av1 + Ai[5]*av2;
971 h_bb[n_el_ents*ie + 2] = x0[2] + Ai[6]*av0 + Ai[7]*av1 + Ai[8]*av2;
972 h_bb[n_el_ents*ie + 3] = ab[0].min;
973 h_bb[n_el_ents*ie + 4] = ab[1].min;
974 h_bb[n_el_ents*ie + 5] = ab[2].min;
975 h_bb[n_el_ents*ie + 6] = ab[0].max;
976 h_bb[n_el_ents*ie + 7] = ab[1].max;
977 h_bb[n_el_ents*ie + 8] = ab[2].max;
978
979 // Expand the rotated-frame bounds by aabb_diag_len, then premultiply
980 // A by the scaling matrix L = diag(di0,di1,di2) to obtain L*A. This
981 // maps a physical displacement from the OBB center to [-1,1]^3.
982 tb[0].min -= aabb_diag_len;
983 tb[0].max += aabb_diag_len;
984 tb[1].min -= aabb_diag_len;
985 tb[1].max += aabb_diag_len;
986 tb[2].min -= aabb_diag_len;
987 tb[2].max += aabb_diag_len;
988 const double di0 = 2/((1+tol)*(tb[0].max-tb[0].min)),
989 di1 = 2/((1+tol)*(tb[1].max-tb[1].min)),
990 di2 = 2/((1+tol)*(tb[2].max-tb[2].min));
991 h_bb[n_el_ents*ie + 9 ]=di0*A[0];
992 h_bb[n_el_ents*ie + 10]=di0*A[1];
993 h_bb[n_el_ents*ie + 11]=di0*A[2];
994 h_bb[n_el_ents*ie + 12]=di1*A[3];
995 h_bb[n_el_ents*ie + 13]=di1*A[4];
996 h_bb[n_el_ents*ie + 14]=di1*A[5];
997 h_bb[n_el_ents*ie + 15]=di2*A[6];
998 h_bb[n_el_ents*ie + 16]=di2*A[7];
999 h_bb[n_el_ents*ie + 17]=di2*A[8];
1000 }
1001}
1002
1004 const double *const elx[3],
1005 const unsigned n,
1006 const uint nel,
1007 const unsigned m,
1008 const double bbox_rel_size_inc,
1009 const uint local_hash_size,
1010 const uint global_hash_size,
1011 const int rD,
1012 const Vector *aabb_sz_inc)
1013{
1014 // compute element bounding boxes.
1015 const bool store_obb = obb_check;
1016 const int sd = 3;
1017 const int n_box_ents = store_obb ? (3*sd + sd*sd) : (2*sd);
1018 devs.bb.SetSize(nel*n_box_ents);
1019 if (rD == 1)
1020 {
1021 obboxedge_calc_3(devs.bb, elx, n, nel, m, bbox_rel_size_inc, store_obb);
1022 }
1023 else if (rD == 2)
1024 {
1025 obboxsurf_calc_3(devs.bb, elx, n, nel, m, bbox_rel_size_inc, store_obb);
1026 }
1027 else
1028 {
1029 MFEM_ABORT("FindPointsGSLIB::FindPointsSurfSetup3: rD must be 1 or 2");
1030 }
1031
1032 auto h_bb = devs.bb.HostReadWrite();
1033 VerifyAABBPadLayout(aabb_sz_inc, nel, sd);
1034 const int aabb_sz_inc_size = aabb_sz_inc ? aabb_sz_inc->Size() : 0;
1035
1036 Vector elmin(3*nel), elmax(3*nel);
1037 for (uint i = 0; i < nel; i++)
1038 {
1039 const int min_off = (store_obb ? sd : 0) + n_box_ents*i;
1040 const int max_off = (store_obb ? 2*sd : sd) + n_box_ents*i;
1041 for (int d = 0; d < 3; d++)
1042 {
1043 const double pad = GetAABBPad(aabb_sz_inc, aabb_sz_inc_size,
1044 nel, sd, i, d);
1045 if (pad > 0.0)
1046 {
1047 h_bb[min_off + d] -= pad;
1048 h_bb[max_off + d] += pad;
1049 }
1050 }
1051 elmin(i) = h_bb[min_off + 0];
1052 elmin(i + nel) = h_bb[min_off + 1];
1053 elmin(i + 2*nel) = h_bb[min_off + 2];
1054 elmax(i) = h_bb[max_off + 0];
1055 elmax(i + nel) = h_bb[max_off + 1];
1056 elmax(i + 2*nel) = h_bb[max_off + 2];
1057 }
1058
1059 // Skip local map construction for empty partition.
1060 if (nel > 0)
1061 {
1062 BBoxTensorGridMap bbmap(elmin, elmax, nel, 3, local_hash_size, true);
1063 devs.lh_min.HostWrite(); devs.lh_min = bbmap.GetGridMin();
1064 devs.lh_fac.HostWrite(); devs.lh_fac = bbmap.GetGridFac();
1065 devs.lh_offset.HostWrite(); devs.lh_offset = bbmap.GetGridMap();
1066 devs.lh_nx = bbmap.GetGridN()[0];
1067 }
1068 else
1069 {
1070 devs.lh_min.SetSize(sd);
1071 devs.lh_min = 0.0;
1072 devs.lh_fac.SetSize(sd);
1073 devs.lh_fac = 0.0;
1074 devs.lh_offset.SetSize(2);
1075 devs.lh_offset = 1;
1076 devs.lh_nx = 1;
1077 }
1078
1079#ifdef MFEM_USE_MPI
1080 // build global map
1081 if (gsl_comm->np > 1)
1082 {
1083 GlobalBBoxTensorGridMap gbbmap(gsl_comm->c, elmin, elmax,
1084 nel, 3, global_hash_size, true);
1085 devs.gh_min = gbbmap.GetGridMin();
1086 devs.gh_fac = gbbmap.GetGridFac();
1087 devs.gh_offset = gbbmap.GetGridMap();
1088 devs.gh_nx = gbbmap.GetGridN()[0];
1089 }
1090#endif
1091}
1092
1094 const double *const elx[2],
1095 const unsigned n,
1096 const uint nel,
1097 const unsigned m,
1098 const double bbox_rel_size_inc,
1099 const uint local_hash_size,
1100 const uint global_hash_size,
1101 const Vector *aabb_sz_inc)
1102{
1103 // compute element bounding boxes.
1104 const bool store_obb = obb_check;
1105 const int sd = 2;
1106 const int n_box_ents = store_obb ? (3*sd + sd*sd) : (2*sd);
1107 devs.bb.SetSize(nel*n_box_ents);
1108 obboxedge_calc_2(devs.bb, elx, n, nel, m, bbox_rel_size_inc, store_obb);
1109
1110 auto h_bb = devs.bb.HostReadWrite();
1111 VerifyAABBPadLayout(aabb_sz_inc, nel, sd);
1112 const int aabb_sz_inc_size = aabb_sz_inc ? aabb_sz_inc->Size() : 0;
1113
1114 Vector elmin(2*nel), elmax(2*nel);
1115 for (uint i = 0; i < nel; i++)
1116 {
1117 const int min_off = (store_obb ? sd : 0) + n_box_ents*i;
1118 const int max_off = (store_obb ? 2*sd : sd) + n_box_ents*i;
1119 for (int d = 0; d < 2 && aabb_sz_inc; d++)
1120 {
1121 const double pad = GetAABBPad(aabb_sz_inc, aabb_sz_inc_size,
1122 nel, sd, i, d);
1123 if (pad > 0.0)
1124 {
1125 h_bb[min_off+d] -= pad;
1126 h_bb[max_off+d] += pad;
1127 }
1128 }
1129 elmin(i) = h_bb[min_off + 0];
1130 elmin(i + nel) = h_bb[min_off + 1];
1131 elmax(i) = h_bb[max_off + 0];
1132 elmax(i + nel) = h_bb[max_off + 1];
1133 }
1134
1135 if (nel > 0)
1136 {
1137 BBoxTensorGridMap bbmap(elmin, elmax, nel, 2, local_hash_size, true);
1138 devs.lh_min.HostWrite(); devs.lh_min = bbmap.GetGridMin();
1139 devs.lh_fac.HostWrite(); devs.lh_fac = bbmap.GetGridFac();
1140 devs.lh_offset.HostWrite(); devs.lh_offset = bbmap.GetGridMap();
1141 devs.lh_nx = bbmap.GetGridN()[0];
1142 }
1143 else
1144 {
1145 devs.lh_min.SetSize(sd);
1146 devs.lh_min = 0.0;
1147 devs.lh_fac.SetSize(sd);
1148 devs.lh_fac = 0.0;
1149 devs.lh_offset.SetSize(2);
1150 devs.lh_offset = 1;
1151 devs.lh_nx = 1;
1152 }
1153
1154#ifdef MFEM_USE_MPI
1155 // build global map
1156 if (gsl_comm->np > 1)
1157 {
1158 GlobalBBoxTensorGridMap gbbmap(gsl_comm->c, elmin, elmax,
1159 nel, 2, global_hash_size, true);
1160 devs.gh_min = gbbmap.GetGridMin();
1161 devs.gh_fac = gbbmap.GetGridFac();
1162 devs.gh_offset = gbbmap.GetGridMap();
1163 devs.gh_nx = gbbmap.GetGridN()[0];
1164 }
1165#endif
1166}
1167
1168// evaluate the basis function, 1st derivative, and 2nd derivative at
1169// x for the i-th lagrange basis function defined by the gll points z and
1170// lagrange coefficients
1171static void lagrange_eval_second_derivative(double *p0, double x, int i,
1172 const double *z,
1173 const double *lagrangeCoeff,
1174 int pN)
1175{
1176 double u0 = 1, u1 = 0, u2 = 0;
1177 for (int j=0; j<pN; ++j)
1178 {
1179 if (i!=j)
1180 {
1181 double d_j = 2 * (x-z[j]);
1182 u2 = d_j * u2 + u1;
1183 u1 = d_j * u1 + u0;
1184 u0 = d_j * u0;
1185 }
1186 }
1187 double *p1 = p0 + pN, *p2 = p0 + 2 * pN;
1188 p0[i] = lagrangeCoeff[i] * u0;
1189 p1[i] = 2.0 * lagrangeCoeff[i] * u1;
1190 p2[i] = 8.0 * lagrangeCoeff[i] * u2;
1191}
1192
1194 const double bbox_rel_size_inc,
1195 const double newt_tol)
1196{
1197 SetupSurfBase(m, bbox_rel_size_inc, nullptr, newt_tol);
1198}
1199
1201 const Vector &aabb_sz_inc,
1202 const double newt_tol)
1203{
1204 SetupSurfBase(m, 0.0, &aabb_sz_inc, newt_tol);
1205}
1206
1208 const double bbox_rel_size_inc,
1209 const Vector *aabb_sz_inc,
1210 const double newt_tol)
1211{
1212 // EnsureNodes call could be useful if the mesh is 1st order and has no gridfunction defined
1213 MFEM_VERIFY(m.GetNodes() != nullptr, "Mesh nodes are required.");
1214
1215 // call FreeData if FindPointsGSLIB::Setup has been called already
1216 if (setupflag) { FreeData(); }
1217 obb_check = (aabb_sz_inc == nullptr);
1218
1219 SetupCrystal();
1220
1221 mesh = &m;
1222 dim = mesh->Dimension(); // This is reference dimension
1223 spacedim = mesh->SpaceDimension(); // This is physical dimension
1224 MFEM_VERIFY(dim < 3, "Configuration not supported yet.");
1225 MFEM_VERIFY(dim < spacedim,
1226 "Surface setup is only for surface meshes.");
1227
1228 bool supported_surf_elem = true;
1229 for (int e = 0; e < mesh->GetNE() && supported_surf_elem; e++)
1230 {
1231 const Element::Type t = mesh->GetElementType(e);
1232 supported_surf_elem = (t == Element::SEGMENT ||
1234 t == Element::TRIANGLE);
1235 }
1236 MFEM_VERIFY(supported_surf_elem,
1237 "FindPointsGSLIB surface mesh support: only SEGMENT, "
1238 "QUADRILATERAL, and TRIANGLE elements are supported.");
1239 MFEM_VERIFY(dim < spacedim,
1240 "FindPointsGSLIB surface setup only supports surface meshes.");
1241
1242 const int meshOrder = m.GetNodes()->FESpace()->GetMaxElementOrder();
1243 unsigned dof1D = meshOrder + 1;
1244
1247
1250 DEV.dof1d = (int)dof1D;
1251 DEV.newt_tol = newt_tol;
1252
1253 unsigned nr = dof1D;
1254 unsigned mr = 2*dof1D;
1255 if (spacedim==2)
1256 {
1257 double * const elx[2] =
1258 {
1259 mesh_points_cnt == 0 ? nullptr : &gsl_mesh(0),
1260 mesh_points_cnt == 0 ? nullptr : &gsl_mesh(mesh_points_cnt)
1261 };
1263 elx,
1264 nr,
1266 mr,
1267 bbox_rel_size_inc,
1270 aabb_sz_inc);
1271 }
1272 else if (spacedim==3)
1273 {
1274 double * const elx[3] =
1275 {
1276 mesh_points_cnt == 0 ? nullptr : &gsl_mesh(0),
1277 mesh_points_cnt == 0 ? nullptr : &gsl_mesh(mesh_points_cnt),
1278 mesh_points_cnt == 0 ? nullptr : &gsl_mesh(2*mesh_points_cnt)
1279 };
1281 elx,
1282 nr,
1284 mr,
1285 bbox_rel_size_inc,
1288 dim,
1289 aabb_sz_inc);
1290 }
1291
1292 // If we are applying absolute AABB expansion, compute bdr_tol such that
1293 // any point found within a bounding box gets marked CODE_BORDER even
1294 // when it is not actually on the boundary.
1295 if (aabb_sz_inc)
1296 {
1297 const int n_box_ents = obb_check ?
1298 (3*spacedim + spacedim*spacedim) : (2*spacedim);
1299 const int min_off = obb_check ? spacedim : 0;
1300 const int max_off = obb_check ? 2*spacedim : spacedim;
1301 constexpr double bdr_tol_inflate = 1.01;
1302 double max_diag2 = bdr_tol;
1303 auto h_bb = DEV.bb.HostRead();
1304 for (int e = 0; e < NE_split_total; e++)
1305 {
1306 double diag2 = 0.0;
1307 for (int d = 0; d < spacedim; d++)
1308 {
1309 const double lenx = h_bb[e*n_box_ents + max_off + d] -
1310 h_bb[e*n_box_ents + min_off + d];
1311 diag2 += lenx*lenx;
1312 }
1313 max_diag2 = std::max(max_diag2,
1314 diag2 * bdr_tol_inflate * bdr_tol_inflate);
1315 }
1316#ifdef MFEM_USE_MPI
1317 MPI_Allreduce(MPI_IN_PLACE, &max_diag2, 1, MPI_DOUBLE, MPI_MAX,
1318 gsl_comm->c);
1319#endif
1320 bdr_tol = max_diag2; // this is cube/square diagonal length squared
1321 }
1322
1323 // Compute avg element size in the mesh to set surface distance tolerance.
1324 DEV.surf_dist_tol = 0.0;
1325 for (int e = 0; e < mesh->GetNE(); e++)
1326 {
1328 }
1329 int nelem = NE_split_total;
1330#ifdef MFEM_USE_MPI
1331 MPI_Allreduce(MPI_IN_PLACE, &DEV.surf_dist_tol, 1, MPI_DOUBLE, MPI_SUM,
1332 gsl_comm->c);
1333 MPI_Allreduce(MPI_IN_PLACE, &nelem, 1, MPI_INT, MPI_SUM, gsl_comm->c);
1334#endif
1335 DEV.surf_dist_tol /= nelem;
1336 DEV.surf_dist_tol *= 1e-16; // dist^2 tolerance so we use (1e-8)^2.
1337
1338 // Setup gll points and lagrange coefficient data for findpoints
1339 Vector gll1dtemp(DEV.dof1d),
1340 lagcoefftemp(DEV.dof1d),
1341 wtendtemp(6*DEV.dof1d);
1342
1343 gslib::lobatto_nodes(gll1dtemp.GetData(), DEV.dof1d);
1344 gslib::gll_lag_setup(lagcoefftemp.GetData(), DEV.dof1d);
1345
1346 for (int i=0; i<DEV.dof1d; i++)
1347 {
1348 lagrange_eval_second_derivative(wtendtemp.GetData(), -1.0, i,
1349 gll1dtemp.GetData(),
1350 lagcoefftemp.GetData(), DEV.dof1d);
1351 lagrange_eval_second_derivative(wtendtemp.GetData()+3*DEV.dof1d, 1.0, i,
1352 gll1dtemp.GetData(),
1353 lagcoefftemp.GetData(), DEV.dof1d);
1354 }
1355
1357 auto h_wtend = DEV.wtend.HostWrite();
1358 for (int i = 0; i < 6*DEV.dof1d; i++) { h_wtend[i] = wtendtemp[i]; }
1359
1361 auto h_gll1d = DEV.gll1d.HostWrite();
1362 for (int i = 0; i < DEV.dof1d; i++) { h_gll1d[i] = gll1dtemp[i]; }
1363
1365 auto h_lagcoeff = DEV.lagcoeff.HostWrite();
1366 for (int i = 0; i < DEV.dof1d; i++) { h_lagcoeff[i] = lagcoefftemp[i]; }
1367
1368 DEV.cr = cr;
1369 setupflag = true;
1370}
1371
1373 const int point_pos_ordering)
1374{
1375 MFEM_VERIFY(setupflag, "Use FindPointsGSLIB::Setup before finding points.");
1376 if (dim != spacedim)
1377 {
1378 FindPointsSurf(point_pos, point_pos_ordering);
1379 return;
1380 }
1381 bool dev_mode = (point_pos.UseDevice() && Device::IsEnabled());
1382 points_cnt = point_pos.Size() / dim;
1388
1389 bool tensor_product_only = mesh->GetNE() == 0 ||
1390 (mesh->GetNumGeometries(dim) == 1 &&
1393#ifdef MFEM_USE_MPI
1394 MPI_Allreduce(MPI_IN_PLACE, &tensor_product_only, 1, MFEM_MPI_CXX_BOOL,
1395 MPI_LAND, gsl_comm->c);
1396#endif
1397
1398 if (dev_mode && tensor_product_only)
1399 {
1400#if GSLIB_RELEASE_VERSION == 10007
1402 {
1403 MFEM_ABORT("Either update to gslib v1.0.9 for GPU support "
1404 "or use SetGPUtoCPUFallback to use host-functions. See "
1405 "INSTALL for instructions to update GSLIB.");
1406 }
1407#else
1408 FindPointsOnDevice(point_pos, point_pos_ordering);
1409 return;
1410#endif
1411 }
1412
1413 auto pp = point_pos.HostRead();
1414 auto xvFill = [&](const double *xv_base[], unsigned xv_stride[])
1415 {
1416 for (int d = 0; d < dim; d++)
1417 {
1418 if (point_pos_ordering == Ordering::byNODES)
1419 {
1420 xv_base[d] = pp + d*points_cnt;
1421 xv_stride[d] = sizeof(double);
1422 }
1423 else
1424 {
1425 xv_base[d] = pp + d;
1426 xv_stride[d] = dim*sizeof(double);
1427 }
1428 }
1429 };
1430
1431 if (dim == 2)
1432 {
1433 auto *findptsData = (gslib::findpts_data_2 *)this->fdataD;
1434 const double *xv_base[2];
1435 unsigned xv_stride[2];
1436 xvFill(xv_base, xv_stride);
1437 findpts_2(gsl_code.GetData(), sizeof(unsigned int),
1438 gsl_proc.GetData(), sizeof(unsigned int),
1439 gsl_elem.GetData(), sizeof(unsigned int),
1440 gsl_ref.GetData(), sizeof(double) * dim,
1441 gsl_dist.GetData(), sizeof(double),
1442 xv_base, xv_stride, points_cnt, findptsData);
1443 }
1444 else // dim == 3
1445 {
1446 auto *findptsData = (gslib::findpts_data_3 *)this->fdataD;
1447 const double *xv_base[3];
1448 unsigned xv_stride[3];
1449 xvFill(xv_base, xv_stride);
1450 findpts_3(gsl_code.GetData(), sizeof(unsigned int),
1451 gsl_proc.GetData(), sizeof(unsigned int),
1452 gsl_elem.GetData(), sizeof(unsigned int),
1453 gsl_ref.GetData(), sizeof(double) * dim,
1454 gsl_dist.GetData(), sizeof(double),
1455 xv_base, xv_stride, points_cnt,
1456 findptsData);
1457 }
1458
1459 // Set the element number and reference position to 0 for points not found
1460 for (int i = 0; i < points_cnt; i++)
1461 {
1462 if (gsl_code[i] == 2 ||
1463 (gsl_code[i] == 1 && gsl_dist(i) > bdr_tol))
1464 {
1465 gsl_elem[i] = 0;
1466 for (int d = 0; d < dim; d++) { gsl_ref(i*dim + d) = -1.; }
1467 gsl_code[i] = 2;
1468 gsl_proc[i] = gsl_comm->id;
1469 }
1470 }
1471
1472 // Map element number for simplices, and ref_pos from [-1,1] to [0,1] for
1473 // both simplices and quads. Also sets code to 1 for points found on element
1474 // faces/edges.
1476}
1477
1478#if GSLIB_RELEASE_VERSION >= 10009
1479
1480slong lfloor(double x) { return floor(x); }
1481
1482// Local hash mesh index in 1D for a given point
1483ulong hash_index_1(double low, double fac, ulong n, double x)
1484{
1485 const slong i = lfloor((x - low) * fac);
1486 return i < 0 ? 0 : (n - 1 < (ulong)i ? n - 1 : (ulong)i);
1487}
1488
1489// Local hash mesh index in 3D for a given point
1490ulong hash_index_3(const gslib::hash_data_3 *p, const double x[3])
1491{
1492 const ulong n = p->hash_n;
1493 return (hash_index_1(p->bnd[2].min, p->fac[2], n, x[2]) * n +
1494 hash_index_1(p->bnd[1].min, p->fac[1], n, x[1])) *
1495 n +
1496 hash_index_1(p->bnd[0].min, p->fac[0], n, x[0]);
1497}
1498
1499// Local hash mesh index in 2D for a given point
1500ulong hash_index_2(const gslib::hash_data_2 *p, const double x[2])
1501{
1502 const ulong n = p->hash_n;
1503 return (hash_index_1(p->bnd[1].min, p->fac[1], n, x[1])) * n
1504 + hash_index_1(p->bnd[0].min, p->fac[0], n, x[0]);
1505}
1506
1508{
1509 auto *findptsData3 = (gslib::findpts_data_3 *)this->fdataD;
1510 auto *findptsData2 = (gslib::findpts_data_2 *)this->fdataD;
1511
1512 DEV.newt_tol = dim == 2 ? findptsData2->local.tol : findptsData3->local.tol;
1513 if (dim == 3)
1514 {
1515 DEV.hash3 = &findptsData3->hash;
1516 }
1517 else
1518 {
1519 DEV.hash2 = &findptsData2->hash;
1520 }
1521 DEV.cr = dim == 2 ? &findptsData2->cr : &findptsData3->cr;
1522
1523 gsl_mesh.UseDevice(true);
1524
1525 int n_box_ents = 3*dim + dim*dim;
1526 DEV.bb.UseDevice(true); DEV.bb.SetSize(n_box_ents*NE_split_total);
1527 auto p_bb = DEV.bb.HostWrite();
1529
1530 const int dim2 = dim*dim;
1531 if (dim == 3)
1532 {
1533 for (int e = 0; e < NE_split_total; e++)
1534 {
1535 auto box = findptsData3->local.obb[e];
1536 for (int d = 0; d < dim; d++)
1537 {
1538 p_bb[n_box_ents*e + d] = box.c0[d];
1539 p_bb[n_box_ents*e + dim + d] = box.x[d].min;
1540 p_bb[n_box_ents*e + 2*dim + d] = box.x[d].max;
1541 elmin(d*NE_split_total + e) = box.x[d].min;
1542 elmax(d*NE_split_total + e) = box.x[d].max;
1543 }
1544 for (int d = 0; d < dim2; ++d)
1545 {
1546 p_bb[n_box_ents*e + 3*dim + d] = box.A[d];
1547 }
1548 }
1549 }
1550 else
1551 {
1552 for (int e = 0; e < NE_split_total; e++)
1553 {
1554 auto box = findptsData2->local.obb[e];
1555 for (int d = 0; d < dim; d++)
1556 {
1557 p_bb[n_box_ents*e + d] = box.c0[d];
1558 p_bb[n_box_ents*e + dim + d] = box.x[d].min;
1559 p_bb[n_box_ents*e + 2*dim + d] = box.x[d].max;
1560 elmin(d*NE_split_total + e) = box.x[d].min;
1561 elmax(d*NE_split_total + e) = box.x[d].max;
1562 }
1563 for (int d = 0; d < dim2; ++d)
1564 {
1565 p_bb[n_box_ents*e + 3*dim + d] = box.A[d];
1566 }
1567 }
1568 }
1569
1572 if (dim == 2)
1573 {
1574 auto hash = findptsData2->local.hd;
1575 auto p_loc_hash_min = DEV.lh_min.HostWrite();
1576 auto p_loc_hash_fac = DEV.lh_fac.HostWrite();
1577 for (int d = 0; d < dim; d++)
1578 {
1579 p_loc_hash_min[d] = hash.bnd[d].min;
1580 p_loc_hash_fac[d] = hash.fac[d];
1581 }
1582 DEV.lh_nx = hash.hash_n;
1583 }
1584 else
1585 {
1586 auto hash = findptsData3->local.hd;
1587 auto p_loc_hash_min = DEV.lh_min.HostWrite();
1588 auto p_loc_hash_fac = DEV.lh_fac.HostWrite();
1589 for (int d = 0; d < dim; d++)
1590 {
1591 p_loc_hash_min[d] = hash.bnd[d].min;
1592 p_loc_hash_fac[d] = hash.fac[d];
1593 }
1594 DEV.lh_nx = hash.hash_n;
1595 }
1596
1597 int h_o_size = dim == 2 ?
1598 findptsData2->local.hd.offset[(int)std::pow(DEV.lh_nx, dim)] :
1599 findptsData3->local.hd.offset[(int)std::pow(DEV.lh_nx, dim)];
1600
1601 DEV.lh_offset.SetSize(h_o_size);
1602 auto p_ou_offset = DEV.lh_offset.HostWrite();
1603 for (int i = 0; i < h_o_size; i++)
1604 {
1605 p_ou_offset[i] = dim == 2 ? findptsData2->local.hd.offset[i] :
1606 findptsData3->local.hd.offset[i];
1607 }
1608
1609 DEV.wtend.UseDevice(true);
1611 auto *h_wtend = DEV.wtend.HostWrite();
1612 double *src_wtend = dim == 2 ? findptsData2->local.fed.wtend[0] :
1613 findptsData3->local.fed.wtend[0];
1614 for (int i = 0; i < 6*DEV.dof1d; i++) { h_wtend[i] = src_wtend[i]; }
1615
1616 // Get gll points
1617 DEV.gll1d.UseDevice(true);
1619 auto *h_gll1d = DEV.gll1d.HostWrite();
1620 double *src_gll1d = dim == 2 ? findptsData2->local.fed.z[0] :
1621 findptsData3->local.fed.z[0];
1622 for (int i = 0; i < DEV.dof1d; i++) { h_gll1d[i] = src_gll1d[i]; }
1623
1624 DEV.lagcoeff.UseDevice(true);
1626 auto *h_lagcoeff = DEV.lagcoeff.HostWrite();
1627 double *src_lagcoeff = dim == 2 ? findptsData2->local.fed.lag_data[0] :
1628 findptsData3->local.fed.lag_data[0];
1629 for (int i = 0; i < DEV.dof1d; i++) { h_lagcoeff[i] = src_lagcoeff[i]; }
1630
1631 DEV.setup_device = true;
1632}
1633
1635 const int point_pos_ordering)
1636{
1637 if (!DEV.setup_device)
1638 {
1639 SetupDevice();
1640 }
1641 DEV.find_device = true;
1642
1643 const int id = gsl_comm->id, np = gsl_comm->np;
1644 const int ddim = dim;
1645
1648 gsl_ref.UseDevice(true);
1649 gsl_dist.UseDevice(true);
1650
1651 // Initialize proc to id for all points. Code and dist will be done
1652 // inside kernels.
1654 gsl_proc = id;
1655
1656 if (dim == 2)
1657 {
1658 FindPointsLocal2(point_pos, point_pos_ordering, gsl_code, gsl_elem,
1660 }
1661 else
1662 {
1663 FindPointsLocal3(point_pos, point_pos_ordering, gsl_code, gsl_elem,
1665 }
1666
1667 // Tolerance for point to be marked as on element edge/face based on the
1668 // obtained reference-space coordinates.
1669 double rbtol = 1e-12; // must match MapRefPosAndElemIndices for consistency
1670
1671 if (np == 1)
1672 {
1673 auto d_gsl_code = gsl_code.ReadWrite(); // no memory transfer
1674 auto d_gsl_ref = gsl_ref.Read(); // no memory transfer
1675 auto d_gsl_dist = gsl_dist.Read(); // no memory transfer
1676 auto d_gsl_elem = gsl_elem.Read(); // no memory transfer
1677 auto d_gsl_mfem_ref = gsl_mfem_ref.Write();
1678 auto d_gsl_mfem_elem = gsl_mfem_elem.Write();
1679
1680 const int pts_cnt = points_cnt;
1681 const double bdr_t = bdr_tol;
1682
1683 FindPointsDeviceSetCode(pts_cnt, ddim, rbtol, bdr_t, d_gsl_code,
1684 d_gsl_ref, d_gsl_dist, d_gsl_elem,
1685 d_gsl_mfem_ref, d_gsl_mfem_elem);
1686 return;
1687 }
1688
1689#ifdef MFEM_USE_MPI
1690 MPI_Barrier(gsl_comm->c);
1691#endif
1692 /* send unfound and border points to global hash cells */
1693 struct gslib::array hash_pt, src_pt, out_pt;
1694
1695 struct srcPt_t
1696 {
1697 double x[3];
1698 unsigned int index, proc;
1699 };
1700
1701 struct outPt_t
1702 {
1703 double r[3], dist2;
1704 unsigned int index, code, el, proc;
1705 };
1706
1707 {
1708 // Sync from device to host to send points to other ranks
1709 auto h_gsl_code = gsl_code.HostReadWrite(); // read now, write later
1710 auto h_pp = point_pos.HostRead();
1711
1712 int index;
1713 struct srcPt_t *pt;
1714
1715 array_init(struct srcPt_t, &hash_pt, points_cnt);
1716 pt = (struct srcPt_t *)hash_pt.ptr;
1717
1718 auto x = new double[dim];
1719 for (index = 0; index < points_cnt; ++index)
1720 {
1721 const int code_i = h_gsl_code[index];
1722 if (code_i != CODE_INTERNAL)
1723 {
1724 for (int d = 0; d < dim; ++d)
1725 {
1726 int idx = point_pos_ordering == 0 ?
1727 index + d*points_cnt :
1728 index*dim + d;
1729 x[d] = h_pp[idx];
1730 }
1731 const auto hi = dim == 2 ? hash_index_2(DEV.hash2, x) :
1733 for (int d = 0; d < dim; ++d)
1734 {
1735 pt->x[d] = x[d];
1736 }
1737 pt->index = index;
1738 pt->proc = hi % np;
1739 ++pt;
1740 }
1741 }
1742 delete[] x;
1743 hash_pt.n = pt - (struct srcPt_t *)hash_pt.ptr;
1744 sarray_transfer(struct srcPt_t, &hash_pt, proc, 1, DEV.cr);
1745 }
1746#ifdef MFEM_USE_MPI
1747 MPI_Barrier(gsl_comm->c);
1748#endif
1749
1750 /* look up points in hash cells, route to possible procs */
1751 {
1752 const unsigned int *const hash_offset = dim == 2 ? DEV.hash2->offset :
1753 DEV.hash3->offset;
1754 int count = 0;
1755 unsigned int *proc, *proc_p;
1756 const struct srcPt_t *p = (struct srcPt_t *)hash_pt.ptr,
1757 *const pe = p + hash_pt.n;
1758 struct srcPt_t *q;
1759
1760 for (; p != pe; ++p)
1761 {
1762 const int hi = dim == 2 ? hash_index_2(DEV.hash2, p->x)/np :
1763 hash_index_3(DEV.hash3, p->x)/np;
1764 const int i = hash_offset[hi], ie = hash_offset[hi + 1];
1765 count += ie - i;
1766 }
1767
1768 Array<unsigned int> proc_array(count);
1769 proc = proc_array.GetData();
1770 proc_p = proc;
1771 array_init(struct srcPt_t, &src_pt, count);
1772 q = (struct srcPt_t *)src_pt.ptr;
1773
1774 p = (struct srcPt_t *)hash_pt.ptr;
1775 for (; p != pe; ++p)
1776 {
1777 const int hi = dim == 2 ? hash_index_2(DEV.hash2, p->x)/np :
1778 hash_index_3(DEV.hash3, p->x)/np;
1779 int i = hash_offset[hi];
1780 const int ie = hash_offset[hi + 1];
1781 for (; i != ie; ++i)
1782 {
1783 const unsigned int pp = hash_offset[i];
1784 /* don't send back to where it just came from */
1785 if (pp == p->proc)
1786 {
1787 continue;
1788 }
1789 *proc_p++ = pp;
1790 *q++ = *p;
1791 }
1792 }
1793
1794 array_free(&hash_pt);
1795 src_pt.n = proc_p - proc;
1796
1797 sarray_transfer_ext(struct srcPt_t, &src_pt, proc, sizeof(uint), DEV.cr);
1798 }
1799#ifdef MFEM_USE_MPI
1800 MPI_Barrier(gsl_comm->c);
1801#endif
1802
1803 /* look for other procs' points, send back */
1804 {
1805 int n = src_pt.n;
1806 const struct srcPt_t *spt;
1807 struct outPt_t *opt;
1808 array_init(struct outPt_t, &out_pt, n);
1809 out_pt.n = n;
1810 spt = (struct srcPt_t *)src_pt.ptr;
1811 opt = (struct outPt_t *)out_pt.ptr;
1812 for (; n; --n, ++spt, ++opt)
1813 {
1814 opt->index = spt->index;
1815 opt->proc = spt->proc;
1816 }
1817 spt = (struct srcPt_t *)src_pt.ptr;
1818 opt = (struct outPt_t *)out_pt.ptr;
1819
1820 n = out_pt.n;
1821 Vector gsl_ref_l, gsl_dist_l;
1822 gsl_ref_l.UseDevice(true); gsl_ref_l.SetSize(n*dim);
1823 gsl_dist_l.UseDevice(true); gsl_dist_l.SetSize(n);
1824
1825 Vector point_pos_l;
1826 point_pos_l.UseDevice(true); point_pos_l.SetSize(n*dim);
1827 auto h_ptr_ppl = point_pos_l.HostWrite();
1828
1829 Array<unsigned int> gsl_code_l(n), gsl_elem_l(n);
1830
1831 for (int point = 0; point < n; ++point)
1832 {
1833 for (int d = 0; d < dim; d++)
1834 {
1835 int idx = point_pos_ordering == 0 ? point + d*n : point*dim + d;
1836 h_ptr_ppl[idx] = spt[point].x[d];
1837 }
1838 }
1839
1840 if (dim == 2)
1841 {
1842 FindPointsLocal2(point_pos_l, point_pos_ordering, gsl_code_l,
1843 gsl_elem_l, gsl_ref_l, gsl_dist_l, n);
1844 }
1845 else
1846 {
1847 FindPointsLocal3(point_pos_l, point_pos_ordering, gsl_code_l,
1848 gsl_elem_l, gsl_ref_l, gsl_dist_l, n);
1849 }
1850
1851 gsl_ref_l.HostRead();
1852 gsl_dist_l.HostRead();
1853 gsl_code_l.HostRead();
1854 gsl_elem_l.HostRead();
1855
1856 // unpack arrays into opt
1857 for (int point = 0; point < n; point++)
1858 {
1859 opt[point].code = AsConst(gsl_code_l)[point];
1860 if (opt[point].code == CODE_NOT_FOUND)
1861 {
1862 continue;
1863 }
1864 opt[point].el = AsConst(gsl_elem_l)[point];
1865 opt[point].dist2 = AsConst(gsl_dist_l)[point];
1866 for (int d = 0; d < dim; ++d)
1867 {
1868 opt[point].r[d] = AsConst(gsl_ref_l)[dim * point + d];
1869 }
1870 // for found points set gsl_code using reference space coords.
1872 if (dim == 2)
1873 {
1874 ip.Set2(0.5*opt[point].r[0]+0.5, 0.5*opt[point].r[1]+0.5);
1875 }
1876 else
1877 {
1878 ip.Set3(0.5*opt[point].r[0]+0.5, 0.5*opt[point].r[1]+0.5,
1879 0.5*opt[point].r[2]+0.5);
1880 }
1881 const FiniteElement *fe = mesh->GetNodalFESpace()->GetFE(opt[point].el);
1882 const Geometry::Type gt = fe->GetGeomType();
1883 int setcode = Geometry::CheckPoint(gt, ip, -rbtol) ?
1884 CODE_INTERNAL : CODE_BORDER;
1885 opt[point].code = setcode==CODE_BORDER && opt[point].dist2>bdr_tol ?
1886 CODE_NOT_FOUND : setcode;
1887 }
1888
1889 array_free(&src_pt);
1890
1891 /* group by code to eliminate unfound points */
1892 sarray_sort(struct outPt_t, opt, out_pt.n, code, 0, &DEV.cr->data);
1893
1894 n = out_pt.n;
1895 while (n && opt[n - 1].code == CODE_NOT_FOUND)
1896 {
1897 --n;
1898 }
1899 out_pt.n = n;
1900
1901 sarray_transfer(struct outPt_t, &out_pt, proc, 1, DEV.cr);
1902 }
1903#ifdef MFEM_USE_MPI
1904 MPI_Barrier(gsl_comm->c);
1905#endif
1906
1907 auto h_gsl_code = gsl_code.HostReadWrite();
1908 auto h_gsl_dist = gsl_dist.HostReadWrite();
1909 auto h_gsl_ref = gsl_ref.HostReadWrite();
1910 auto h_gsl_mfem_ref = gsl_mfem_ref.HostReadWrite();
1911 auto h_gsl_elem = gsl_elem.HostReadWrite();
1912 auto h_gsl_mfem_elem = gsl_mfem_elem.HostReadWrite();
1913 auto h_gsl_proc = gsl_proc.HostReadWrite();
1914
1915 /* merge remote results with user data */
1916 {
1917 int n = out_pt.n;
1918 struct outPt_t *opt = (struct outPt_t *)out_pt.ptr;
1919 for (int i = 0; i < n; i++)
1920 {
1921 const int index = opt[i].index;
1922 if (h_gsl_code[index] == CODE_INTERNAL)
1923 {
1924 continue;
1925 }
1926 if (h_gsl_code[index] == CODE_NOT_FOUND ||
1927 opt[i].code == CODE_INTERNAL ||
1928 opt[i].dist2 < h_gsl_dist[index])
1929 {
1930 for (int d = 0; d < dim; ++d)
1931 {
1932 real_t rv = opt[i].r[d];
1933 h_gsl_ref[dim * index + d] = rv;
1934 h_gsl_mfem_ref[dim*index + d] = 0.5*(rv + 1.);
1935 }
1936 h_gsl_dist[index] = opt[i].dist2;
1937 h_gsl_proc[index] = opt[i].proc;
1938 h_gsl_elem[index] = opt[i].el;
1939 h_gsl_mfem_elem[index] = opt[i].el;
1940 h_gsl_code[index] = opt[i].code;
1941 }
1942 }
1943 array_free(&out_pt);
1944 }
1945
1946 // For points found locally, we set gsl_mfem_elem, gsl_mfem_ref, and
1947 // gsl_code since it was not set until now.
1948 for (int index = 0; index < points_cnt; index++)
1949 {
1950 if (h_gsl_code[index] == CODE_NOT_FOUND || h_gsl_proc[index] != id)
1951 {
1952 continue;
1953 }
1954 h_gsl_mfem_elem[index] = h_gsl_elem[index];
1955
1956 bool internal = true;
1957 for (int k = 0; k < dim; k++)
1958 {
1959 double r_val = h_gsl_ref[index * dim + k];
1960 double val = 0.5 * (r_val + 1.0);
1961 h_gsl_mfem_ref[index * dim + k] = val;
1962 if (val < rbtol || val > 1.0 - rbtol)
1963 {
1964 internal = false;
1965 }
1966 }
1967
1968 int setcode = internal ? CODE_INTERNAL : CODE_BORDER;
1969 h_gsl_code[index] = (setcode == CODE_BORDER &&
1970 h_gsl_dist[index] > bdr_tol)
1971 ? CODE_NOT_FOUND
1972 : setcode;
1973 }
1974}
1975
1976struct evalSrcPt_t
1977{
1978 double r[3];
1979 unsigned int index, proc, el;
1980};
1981
1982struct evalOutPt_t
1983{
1984 double out;
1985 unsigned int index, proc;
1986};
1987
1989 Vector &field_out,
1990 const int nel,
1991 const int ncomp,
1992 const int dof1Dsol,
1993 const int ordering)
1994{
1995 field_out.UseDevice(true);
1996 field_out.SetSize(points_cnt*ncomp);
1997 field_out = default_interp_value;
1998
1999 DEV.dof1d_sol = dof1Dsol;
2000 DEV.gll1d_sol.UseDevice(true); DEV.gll1d_sol.SetSize(dof1Dsol);
2002 if (DEV.dof1d_sol != DEV.dof1d || !DEV.find_device)
2003 {
2004 gslib::lobatto_nodes(DEV.gll1d_sol.HostWrite(), dof1Dsol);
2005 gslib::gll_lag_setup(DEV.lagcoeff_sol.HostWrite(), dof1Dsol);
2006 }
2007 else
2008 {
2011 }
2012
2013 // field_out.HostReadWrite(); //Reads in default value from device
2014
2015 struct gslib::array src, outpt;
2016 int nlocal = 0;
2017 /* weed out unfound points, send out */
2018 Array<int> gsl_elem_temp;
2019 Vector gsl_ref_temp;
2020 Array<int> index_temp;
2021 {
2022 int index;
2023 const unsigned int *h_code = gsl_code.HostRead(),
2024 *h_proc = gsl_proc.HostRead(),
2025 *h_el = gsl_elem.HostRead();
2026 const double *h_r = gsl_ref.HostRead();
2027
2028 int numSend = 0;
2029
2030 for (index = 0; index < points_cnt; ++index)
2031 {
2032 numSend += (h_code[index] != CODE_NOT_FOUND &&
2033 h_proc[index] != gsl_comm->id);
2034 nlocal += (h_code[index] != CODE_NOT_FOUND &&
2035 h_proc[index] == gsl_comm->id);
2036 }
2037
2038 gsl_elem_temp.SetSize(nlocal);
2039 gsl_elem_temp.HostWrite();
2040
2041 gsl_ref_temp.SetSize(nlocal*dim);
2042 gsl_ref_temp.UseDevice(true);
2043 gsl_ref_temp.HostWrite();
2044
2045 index_temp.SetSize(nlocal);
2046
2047 evalSrcPt_t *pt;
2048 array_init(evalSrcPt_t, &src, numSend);
2049 pt = (evalSrcPt_t *)src.ptr;
2050
2051 int ctr = 0;
2052 for (index = 0; index < points_cnt; ++index)
2053 {
2054 if (h_code[index] != CODE_NOT_FOUND && h_proc[index] != gsl_comm->id)
2055 {
2056 for (int d = 0; d < dim; ++d)
2057 {
2058 pt->r[d] = h_r[index*dim+d];
2059 }
2060 pt->index = index;
2061 pt->proc = h_proc[index];
2062 pt->el = h_el[index];
2063 ++pt;
2064 }
2065 else if (h_code[index] != CODE_NOT_FOUND &&
2066 h_proc[index] == gsl_comm->id)
2067 {
2068 gsl_elem_temp[ctr] = h_el[index];
2069 for (int d = 0; d < dim; ++d)
2070 {
2071 gsl_ref_temp(dim*ctr+d) = h_r[index*dim+d];
2072 }
2073 index_temp[ctr] = index;
2074 ctr++;
2075 }
2076 }
2077
2078 src.n = pt - (evalSrcPt_t *)src.ptr;
2079 sarray_transfer(evalSrcPt_t, &src, proc, 1, cr);
2080 }
2081
2082 //evaluate points that are already local
2083 {
2084 Vector interp_vals(nlocal*ncomp);
2085 interp_vals.UseDevice(true);
2086
2087 if (dim == 2)
2088 {
2089 InterpolateLocal2(field_in_evec,
2090 gsl_elem_temp,
2091 gsl_ref_temp,
2092 interp_vals,
2093 nlocal, ncomp,
2094 dof1Dsol);
2095 }
2096 else
2097 {
2098 InterpolateLocal3(field_in_evec,
2099 gsl_elem_temp,
2100 gsl_ref_temp,
2101 interp_vals,
2102 nlocal, ncomp,
2103 dof1Dsol);
2104
2105 }
2106#ifdef MFEM_USE_MPI
2107 MPI_Barrier(gsl_comm->c);
2108#endif
2109
2110 auto d_interp_vals = interp_vals.Read();
2111 auto d_index_temp = index_temp.Read();
2112 auto d_field_out = field_out.ReadWrite(); // no-op, already on device.
2113
2114 const int interp_Offset = interp_vals.Size()/ncomp;
2115 const int pts_cnt = points_cnt;
2116
2117 InterpolateDeviceScatter(nlocal, d_index_temp, d_interp_vals,
2118 d_field_out, interp_Offset, ncomp, pts_cnt,
2119 ordering);
2120 }
2121#ifdef MFEM_USE_MPI
2122 MPI_Barrier(gsl_comm->c);
2123#endif
2124
2125 if (gsl_comm->np == 1)
2126 {
2127 array_free(&src);
2128 return;
2129 }
2130
2131 // evaluate points received from other ranks
2132 {
2133 int n = src.n;
2134 const evalSrcPt_t *spt;
2135 spt = (evalSrcPt_t *)src.ptr;
2136
2137 // Copy to host vector
2138 gsl_elem_temp.SetSize(n);
2139 gsl_elem_temp.HostWrite();
2140
2141 gsl_ref_temp.SetSize(n*dim);
2142 gsl_ref_temp.HostWrite();
2143
2144 spt = (evalSrcPt_t *)src.ptr;
2145 for (int i = 0; i < n; i++, ++spt)
2146 {
2147 gsl_elem_temp[i] = spt->el;
2148 for (int d = 0; d < dim; d++)
2149 {
2150 gsl_ref_temp(i*dim + d) = spt->r[d];
2151 }
2152 }
2153
2154 Vector interp_vals(n*ncomp);
2155 interp_vals.UseDevice(true);
2156 if (dim == 2)
2157 {
2158 InterpolateLocal2(field_in_evec,
2159 gsl_elem_temp,
2160 gsl_ref_temp,
2161 interp_vals, n, ncomp, dof1Dsol);
2162 }
2163 else
2164 {
2165 InterpolateLocal3(field_in_evec,
2166 gsl_elem_temp,
2167 gsl_ref_temp,
2168 interp_vals, n, ncomp, dof1Dsol);
2169 }
2170#ifdef MFEM_USE_MPI
2171 MPI_Barrier(gsl_comm->c);
2172#endif
2173 auto h_interp_vals = interp_vals.HostRead();
2174 auto h_field_out = field_out.HostReadWrite();
2175
2176 // Now the interpolated values need to be sent back component wise
2177 int Offset = interp_vals.Size()/ncomp;
2178 for (int i = 0; i < ncomp; i++)
2179 {
2180 spt = (evalSrcPt_t *)src.ptr;
2181 array_init(evalOutPt_t, &outpt, n);
2182 outpt.n = n;
2183 evalOutPt_t *opt = (evalOutPt_t *)outpt.ptr;
2184
2185 for (int j = 0; j < n; j++)
2186 {
2187 opt->index = spt->index;
2188 opt->proc = spt->proc;
2189 opt->out = h_interp_vals[j + Offset*i];
2190 spt++;
2191 opt++;
2192 }
2193
2194 sarray_transfer(struct evalOutPt_t, &outpt, proc, 1, cr);
2195
2196 opt = (evalOutPt_t *)outpt.ptr;
2197 for (size_t index = 0; index < outpt.n; index++)
2198 {
2199 int idx = ordering == Ordering::byNODES ?
2200 opt->index + i*points_cnt :
2201 opt->index*ncomp + i;
2202 h_field_out[idx] = opt->out;
2203 ++opt;
2204 }
2205 array_free(&outpt);
2206 }
2207 array_free(&src);
2208 }
2209 field_out.ReadWrite();
2210 //finished evaluating points received from other processors.
2211}
2212
2213// Hash mesh index in 3D for a given point
2214ulong hash_index_nd(const Vector &hash_min, const Vector &hash_fac,
2215 const int n, const Vector &x)
2216{
2217 const int dim = x.Size();
2218 if (dim == 2)
2219 {
2220 return (hash_index_1(hash_min(1), hash_fac(1), n, x[1])) * n
2221 + hash_index_1(hash_min(0), hash_fac(0), n, x[0]);
2222 }
2223 else if (dim == 3)
2224 {
2225 return (hash_index_1(hash_min(2), hash_fac(2), n, x[2]) * n +
2226 hash_index_1(hash_min(1), hash_fac(1), n, x[1])) * n +
2227 hash_index_1(hash_min(0), hash_fac(0), n, x[0]);
2228 }
2229 else
2230 {
2231 MFEM_ABORT("hash_index_nd only supports 2D and 3D cases.");
2232 }
2233}
2234
2236 int point_pos_ordering)
2237{
2238 MFEM_VERIFY(setupflag, "Use FindPointsGSLIB::Setup before finding points.");
2239 MFEM_VERIFY(dim < spacedim, "FindPointsSurf is only for surface meshes.");
2240
2241 bool supported_surf_elem = true; // future-proof
2242 for (int e = 0; e < mesh->GetNE(); e++)
2243 {
2244 const Element::Type t = mesh->GetElementType(e);
2245 if (t != Element::TRIANGLE &&
2246 t != Element::SEGMENT &&
2248 {
2249 supported_surf_elem = false;
2250 }
2251 }
2252#ifdef MFEM_USE_MPI
2253 MPI_Allreduce(MPI_IN_PLACE, &supported_surf_elem, 1, MFEM_MPI_CXX_BOOL,
2254 MPI_LAND, gsl_comm->c);
2255#endif
2256 MFEM_VERIFY(supported_surf_elem,
2257 "FindPointsGSLIB surface mesh support: only SEGMENT, "
2258 "QUADRILATERAL, and TRIANGLE elements are supported.");
2259
2260 points_cnt = point_pos.Size()/spacedim;
2261
2269
2270 bool use_dev = point_pos.UseDevice();
2271
2272 gsl_ref.UseDevice(use_dev);
2273 gsl_dist.UseDevice(use_dev);
2274
2275 if (spacedim==2)
2276 {
2277 FindPointsEdgeLocal2(point_pos,
2278 point_pos_ordering,
2279 gsl_code,
2280 gsl_elem,
2281 gsl_ref,
2282 gsl_dist,
2283 points_cnt);
2284 }
2285 else
2286 {
2287 if (dim == 1)
2288 {
2289 FindPointsEdgeLocal3(point_pos,
2290 point_pos_ordering,
2291 gsl_code,
2292 gsl_elem,
2293 gsl_ref,
2294 gsl_dist,
2295 points_cnt);
2296 }
2297 else if (dim == 2)
2298 {
2299 FindPointsSurfLocal3(point_pos,
2300 point_pos_ordering,
2301 gsl_code,
2302 gsl_elem,
2303 gsl_ref,
2304 gsl_dist,
2305 points_cnt);
2306
2307 }
2308 }
2309
2310 // tolerance for point to be marked as on element edge/face
2311 const unsigned int id = gsl_comm->id,
2312 np = gsl_comm->np;
2314 for (int i=0; i<points_cnt; i++)
2315 {
2316 gsl_proc[i] = id;
2317 }
2318 // Tolerance for point to be marked as on element edge/face based on the
2319 // obtained reference-space coordinates.
2320 const double rbtol = 1e-12; // must match MapRefPosAndElemIndices
2321
2322 auto d_gsl_code = gsl_code.ReadWrite(use_dev);
2323 auto d_gsl_ref = gsl_ref.Read(use_dev);
2324 auto d_gsl_elem = gsl_elem.Read(use_dev);
2325 auto d_gsl_mfem_ref = gsl_mfem_ref.Write(use_dev);
2326 auto d_gsl_mfem_elem = gsl_mfem_elem.Write(use_dev);
2327 auto d_gsl_dist = gsl_dist.Read(use_dev);
2328 const int ddim = dim;
2329 const double dbdr_tol = bdr_tol;
2330 auto d_split_elem_map = split_element_map.Read(use_dev);
2331 auto d_split_elem_idx = split_element_index.Read(use_dev);
2332 auto d_split_elem_geom = split_element_geom.Read(use_dev);
2333
2334 // Note: we check if the point is on element border and mark it as such.
2335 // We do not mark points as CODE_INTERNAL because the found solution could
2336 // be interior to the element even when the point is not on the surface.
2337 // This case is handled in the kernels.
2338 MFEM_FORALL(index, points_cnt,
2339 {
2340 if (d_gsl_code[index] == CODE_NOT_FOUND) { return; }
2341
2342 if (ddim == 1)
2343 {
2344 // Segment surface: no splitting, direct copy.
2345 d_gsl_mfem_elem[index] = d_gsl_elem[index];
2346 const real_t ipx = 0.5*(d_gsl_ref[index] + 1.0);
2347 d_gsl_mfem_ref[index] = ipx;
2348 if (ipx < rbtol || ipx > 1.0 - rbtol)
2349 {
2350 d_gsl_code[index] = CODE_BORDER;
2351 }
2352 }
2353 else if (ddim == 2)
2354 {
2355 const int loc_id = d_gsl_elem[index];
2356 d_gsl_mfem_elem[index] = d_split_elem_map[loc_id];
2357
2358 if (d_split_elem_geom[loc_id] == (int)Geometry::TRIANGLE)
2359 {
2360 // Each original triangle is split into 3 quads.
2361 const int tri_id = d_split_elem_idx[loc_id]; // 0, 1, or 2
2362
2363 const double u = 0.5*(d_gsl_ref[index*2 + 0] + 1.0);
2364 const double v = 0.5*(d_gsl_ref[index*2 + 1] + 1.0);
2365 double tx, ty;
2366 MapSplitTriangleQuadToTriangle(tri_id, u, v, tx, ty);
2367
2368 d_gsl_mfem_ref[index*2 + 0] = tx;
2369 d_gsl_mfem_ref[index*2 + 1] = ty;
2370
2371 // Triangle edges
2372 if (tx < rbtol || ty < rbtol || tx + ty > 1.0 - rbtol)
2373 {
2374 d_gsl_code[index] = CODE_BORDER;
2375 }
2376 }
2377 else
2378 {
2379 // Quad/segment boundary/end point
2380 const real_t ipx = 0.5*(d_gsl_ref[index*2 + 0] + 1.0);
2381 const real_t ipy = 0.5*(d_gsl_ref[index*2 + 1] + 1.0);
2382 d_gsl_mfem_ref[index*2 + 0] = ipx;
2383 d_gsl_mfem_ref[index*2 + 1] = ipy;
2384 if (ipx < rbtol || ipx > 1.0 - rbtol ||
2385 ipy < rbtol || ipy > 1.0 - rbtol)
2386 {
2387 d_gsl_code[index] = CODE_BORDER;
2388 }
2389 }
2390 }
2391 if (d_gsl_code[index] == CODE_BORDER && d_gsl_dist[index] > dbdr_tol)
2392 {
2393 d_gsl_code[index] = CODE_NOT_FOUND;
2394 }
2395 });
2396 if (np == 1) { return; }
2397
2398#ifdef MFEM_USE_MPI
2399 MPI_Barrier(gsl_comm->c);
2400#endif
2401
2402 /* send unfound and border points to global hash cells */
2403 struct gslib::array hash_pt, src_pt, out_pt;
2404
2405 struct srcPt_t
2406 {
2407 double x[3];
2408 unsigned int index, proc;
2409 };
2410
2411 struct outPt_t
2412 {
2413 double r[2], dist2;
2414 unsigned int index, code, el, proc;
2415 unsigned int mfem_el, loc_id, geom; // for triangles meshes.
2416 };
2417
2418 {
2419 // Sync from device to host to send points to other ranks
2420 auto h_gsl_code = gsl_code.HostReadWrite(); // read now, write later
2421 auto h_pp = point_pos.HostRead();
2422
2423 int index;
2424 struct srcPt_t *pt;
2425
2426 array_init(struct srcPt_t, &hash_pt, points_cnt);
2427 pt = (struct srcPt_t *)hash_pt.ptr;
2428
2429 Vector x(spacedim);
2430 for (index=0; index<points_cnt; ++index)
2431 {
2432 if (h_gsl_code[index] != CODE_INTERNAL)
2433 {
2434 for (int d=0; d<spacedim; ++d)
2435 {
2436 int idx = point_pos_ordering == 0 ?
2437 index + d*points_cnt :
2438 index*spacedim + d;
2439 x[d] = h_pp[idx];
2440 }
2441 const auto hi = hash_index_nd(DEV.gh_min, DEV.gh_fac, DEV.gh_nx, x);
2442 for (int d=0; d<spacedim; ++d)
2443 {
2444 pt->x[d] = x[d];
2445 }
2446 pt->index = index;
2447 pt->proc = hi % np;
2448 ++pt;
2449 }
2450 }
2451 hash_pt.n = pt - (struct srcPt_t *)hash_pt.ptr;
2452 sarray_transfer(struct srcPt_t, &hash_pt, proc, 1, DEV.cr);
2453 }
2454#ifdef MFEM_USE_MPI
2455 MPI_Barrier(gsl_comm->c);
2456#endif
2457
2458 /* look up points in hash cells, route to possible procs */
2459 {
2460 const unsigned int *const hash_offset = DEV.gh_offset.GetData();
2461 int count = 0;
2462 unsigned int *proc, *proc_p;
2463 const struct srcPt_t *p = (struct srcPt_t *)hash_pt.ptr,
2464 *const pe = p + hash_pt.n;
2465 struct srcPt_t *q;
2466 Vector x(spacedim);
2467
2468 for (; p!=pe; ++p)
2469 {
2470 for (int d = 0; d < spacedim; d++) { x[d] = p->x[d]; }
2471 const int hi = hash_index_nd(DEV.gh_min, DEV.gh_fac, DEV.gh_nx, x)/np;
2472 const int i = hash_offset[hi], ie = hash_offset[hi + 1];
2473 count += ie - i;
2474 }
2475
2476 Array<unsigned int> proc_array(count);
2477 proc = proc_array.GetData();
2478 proc_p = proc;
2479 array_init(struct srcPt_t, &src_pt, count);
2480 q = (struct srcPt_t *)src_pt.ptr;
2481 p = (struct srcPt_t *)hash_pt.ptr;
2482 for (; p!=pe; ++p)
2483 {
2484 for (int d = 0; d < spacedim; d++) { x[d] = p->x[d]; }
2485 const int hi = hash_index_nd(DEV.gh_min, DEV.gh_fac, DEV.gh_nx, x)/np;
2486 int i = hash_offset[hi];
2487 const int ie = hash_offset[hi + 1];
2488 for (; i!=ie; ++i)
2489 {
2490 const unsigned int pp = hash_offset[i];
2491 if (pp == p->proc)
2492 {
2493 continue; /* don't send back to source proc */
2494 }
2495 *proc_p++ = pp;
2496 *q++ = *p;
2497 }
2498 }
2499
2500 array_free(&hash_pt);
2501 src_pt.n = proc_p - proc;
2502
2503 sarray_transfer_ext(struct srcPt_t, &src_pt, proc, sizeof(uint), DEV.cr);
2504 }
2505#ifdef MFEM_USE_MPI
2506 MPI_Barrier(gsl_comm->c);
2507#endif
2508
2509 /* look for other procs' points, send back */
2510 {
2511 int n = src_pt.n;
2512 const struct srcPt_t *spt;
2513 struct outPt_t *opt;
2514 array_init(struct outPt_t, &out_pt, n);
2515 out_pt.n = n;
2516 spt = (struct srcPt_t *)src_pt.ptr;
2517 opt = (struct outPt_t *)out_pt.ptr;
2518 for (; n; --n, ++spt, ++opt)
2519 {
2520 opt->index = spt->index;
2521 opt->proc = spt->proc;
2522 }
2523 spt = (struct srcPt_t *)src_pt.ptr;
2524 opt = (struct outPt_t *)out_pt.ptr;
2525
2526 n = out_pt.n;
2527 Vector gsl_ref_l(n*dim), gsl_dist_l(n);
2528 gsl_ref_l.UseDevice(use_dev);
2529 gsl_dist_l.UseDevice(use_dev);
2530
2531 Vector point_pos_l(n*spacedim);
2532 point_pos_l.UseDevice(use_dev);
2533 auto pointl = point_pos_l.HostWrite();
2534
2535 Array<unsigned int> gsl_code_l(n), gsl_elem_l(n);
2536
2537 for (int point=0; point<n; ++point)
2538 {
2539 for (int d=0; d<spacedim; d++)
2540 {
2541 int idx = point_pos_ordering==0 ? point + d*n :
2542 point*spacedim + d;
2543 pointl[idx] = spt[point].x[d];
2544 }
2545 }
2546
2547 if (spacedim==2)
2548 {
2549 FindPointsEdgeLocal2(point_pos_l,
2550 point_pos_ordering,
2551 gsl_code_l,
2552 gsl_elem_l,
2553 gsl_ref_l,
2554 gsl_dist_l,
2555 n);
2556 }
2557 else
2558 {
2559 if (dim == 1)
2560 {
2561 FindPointsEdgeLocal3(point_pos_l,
2562 point_pos_ordering,
2563 gsl_code_l,
2564 gsl_elem_l,
2565 gsl_ref_l,
2566 gsl_dist_l,
2567 n);
2568
2569 }
2570 else
2571 {
2572 FindPointsSurfLocal3(point_pos_l,
2573 point_pos_ordering,
2574 gsl_code_l,
2575 gsl_elem_l,
2576 gsl_ref_l,
2577 gsl_dist_l,
2578 n);
2579 }
2580 }
2581
2582 gsl_ref_l.HostRead();
2583 gsl_dist_l.HostRead();
2584 gsl_code_l.HostRead();
2585 gsl_elem_l.HostRead();
2586 auto h_split_element_map = split_element_map.HostRead();
2587 auto h_split_element_index = split_element_index.HostRead();
2588 auto h_split_element_geom = split_element_geom.HostRead();
2589
2590 // unpack arrays into opt
2591 for (int point=0; point<n; ++point)
2592 {
2593 opt[point].code = AsConst(gsl_code_l)[point];
2594 if (opt[point].code == CODE_NOT_FOUND)
2595 {
2596 continue;
2597 }
2598 opt[point].el = AsConst(gsl_elem_l)[point];
2599 opt[point].dist2 = AsConst(gsl_dist_l)[point];
2600 for (int d = 0; d < dim; ++d)
2601 {
2602 opt[point].r[d] = AsConst(gsl_ref_l)[dim * point + d];
2603 }
2604 {
2605 const int loc_id = AsConst(gsl_elem_l)[point];
2606 opt[point].mfem_el = h_split_element_map[loc_id];
2607 opt[point].loc_id = h_split_element_index[loc_id];
2608 opt[point].geom = h_split_element_geom[loc_id];
2609 }
2610 // Note: we check if the point is on element border and mark it as
2611 // such. We do not mark points as CODE_INTERNAL because the found
2612 // solution could be interior to the element even when the point
2613 // is not on the edge/surface. This case is handled in the kernels.
2614 if (dim == 1)
2615 {
2616 real_t ipx = 0.5*opt[point].r[0]+0.5;
2617 if (ipx < rbtol || ipx > 1.0 - rbtol)
2618 {
2619 opt[point].code = CODE_BORDER;
2620 }
2621 }
2622 else if (dim == 2)
2623 {
2624 if (opt[point].geom == (int)Geometry::TRIANGLE)
2625 {
2626 // opt[point].el and r are for gslib.
2627 const int loc_id = opt[point].loc_id; // 0,1,2
2628 const double u = 0.5*opt[point].r[0] + 0.5;
2629 const double v = 0.5*opt[point].r[1] + 0.5;
2630 double tx, ty;
2631 MapSplitTriangleQuadToTriangle(loc_id, u, v, tx, ty);
2632 // Triangle edge or not.
2633 if (tx < rbtol || ty < rbtol || tx + ty > 1.0 - rbtol)
2634 {
2635 opt[point].code = CODE_BORDER;
2636 }
2637 }
2638 else // quad
2639 {
2640 real_t ipx = 0.5*opt[point].r[0]+0.5;
2641 real_t ipy = 0.5*opt[point].r[1]+0.5;
2642 if (ipx < rbtol || ipx > 1.0 - rbtol ||
2643 ipy < rbtol || ipy > 1.0 - rbtol)
2644 {
2645 opt[point].code = CODE_BORDER;
2646 }
2647 }
2648 }
2649 if (opt[point].code == CODE_BORDER && opt[point].dist2 > bdr_tol)
2650 {
2651 opt[point].code = CODE_NOT_FOUND;
2652 }
2653 }
2654 array_free(&src_pt);
2655
2656 /* group by code to eliminate unfound points */
2657 sarray_sort(struct outPt_t, opt, out_pt.n, code, 0, &DEV.cr->data);
2658
2659 n = out_pt.n;
2660 while (n && opt[n-1].code == CODE_NOT_FOUND)
2661 {
2662 --n;
2663 }
2664 out_pt.n = n;
2665
2666 sarray_transfer(struct outPt_t, &out_pt, proc, 1, DEV.cr);
2667 }
2668#ifdef MFEM_USE_MPI
2669 MPI_Barrier(gsl_comm->c);
2670#endif
2671
2672 auto h_gsl_code = gsl_code.HostReadWrite();
2673 auto h_gsl_dist = gsl_dist.HostReadWrite();
2674 auto h_gsl_ref = gsl_ref.HostReadWrite();
2675 auto h_gsl_mfem_ref = gsl_mfem_ref.HostReadWrite();
2676 auto h_gsl_elem = gsl_elem.HostReadWrite();
2677 auto h_gsl_mfem_elem = gsl_mfem_elem.HostReadWrite();
2678 auto h_gsl_proc = gsl_proc.HostReadWrite();
2679
2680 // /* merge remote results with user data */
2681 {
2682 int n = out_pt.n;
2683 struct outPt_t *opt = (struct outPt_t *)out_pt.ptr;
2684 for (; n; --n, ++opt)
2685 {
2686 const int index = opt->index;
2687 if (h_gsl_code[index] == CODE_INTERNAL)
2688 {
2689 continue;
2690 }
2691 if ( h_gsl_code[index]==CODE_NOT_FOUND
2692 || opt->code==CODE_INTERNAL
2693 || opt->dist2<h_gsl_dist[index] )
2694 {
2695 for (int d=0; d<dim; ++d)
2696 {
2697 h_gsl_ref[dim*index + d] = opt->r[d];
2698 }
2699 h_gsl_dist[index] = opt->dist2;
2700 h_gsl_proc[index] = opt->proc;
2701 h_gsl_elem[index] = opt->el;
2702 h_gsl_code[index] = opt->code;
2703 h_gsl_mfem_elem[index] = opt->mfem_el;
2704
2705 if (dim == 2 && opt->geom == (int)Geometry::TRIANGLE)
2706 {
2707 const int loc_id = opt->loc_id;
2708 const double u = 0.5*opt->r[0] + 0.5;
2709 const double v = 0.5*opt->r[1] + 0.5;
2710 MapSplitTriangleQuadToTriangle(loc_id, u, v,
2711 h_gsl_mfem_ref[dim*index + 0],
2712 h_gsl_mfem_ref[dim*index + 1]);
2713 }
2714 else
2715 {
2716 for (int d=0; d<dim; ++d)
2717 {
2718 h_gsl_mfem_ref[dim*index + d] = 0.5*(opt->r[d] + 1.);
2719 }
2720 }
2721 }
2722 }
2723 array_free(&out_pt);
2724 }
2725#ifdef MFEM_USE_MPI
2726 MPI_Barrier(gsl_comm->c);
2727#endif
2728}
2729
2731 Vector &field_out,
2732 const int nel,
2733 const int ncomp,
2734 const int dof1Dsol,
2735 const int field_out_ordering)
2736{
2737 bool use_dev = field_in.UseDevice();
2738 struct gslib::array src, outpt;
2739 int nlocal = 0;
2740 /* weed out unfound points, send out */
2741 Array<int> gsl_elem_temp;
2742 Vector gsl_ref_temp;
2743 Array<int> index_temp;
2744 {
2745 int index;
2746 const unsigned int *h_code = gsl_code.HostRead(),
2747 *h_proc = gsl_proc.HostRead(),
2748 *h_el = gsl_elem.HostRead();
2749 const double *h_r = gsl_ref.HostRead();
2750
2751 int numSend = 0;
2752
2753 for (index=0; index<points_cnt; ++index)
2754 {
2755 numSend += (h_code[index] != CODE_NOT_FOUND &&
2756 h_proc[index] != gsl_comm->id);
2757 nlocal += (h_code[index] != CODE_NOT_FOUND &&
2758 h_proc[index] == gsl_comm->id);
2759 }
2760
2761 gsl_elem_temp.SetSize(nlocal);
2762 gsl_elem_temp.HostWrite();
2763
2764 gsl_ref_temp.SetSize(nlocal*dim);
2765 gsl_ref_temp.UseDevice(use_dev);
2766 gsl_ref_temp.HostWrite();
2767
2768 index_temp.SetSize(nlocal);
2769
2770 evalSrcPt_t *pt;
2771 array_init(evalSrcPt_t, &src, numSend);
2772 pt = (evalSrcPt_t *)src.ptr;
2773
2774 int ctr = 0;
2775 for (index=0; index<points_cnt; ++index)
2776 {
2777 if (h_code[index]!=CODE_NOT_FOUND && h_proc[index]!=gsl_comm->id)
2778 {
2779 for (int d=0; d<dim; ++d)
2780 {
2781 pt->r[d] = h_r[index*dim + d];
2782 }
2783 pt->index = index;
2784 pt->proc = h_proc[index];
2785 pt->el = h_el[index];
2786 ++pt;
2787 }
2788 else if (h_code[index]!=CODE_NOT_FOUND && h_proc[index]==gsl_comm->id)
2789 {
2790 gsl_elem_temp[ctr] = h_el[index];
2791 for (int d=0; d<dim; ++d)
2792 {
2793 gsl_ref_temp(dim*ctr+d) = h_r[index*dim + d];
2794 }
2795 index_temp[ctr] = index;
2796 ctr++;
2797 }
2798 }
2799
2800 src.n = pt - (evalSrcPt_t *)src.ptr;
2801 sarray_transfer(evalSrcPt_t, &src, proc, 1, cr);
2802 }
2803
2804 //evaluate points that are already local
2805 {
2806 Vector interp_vals(nlocal*ncomp);
2807 interp_vals.UseDevice(use_dev);
2808
2809 if (dim == 1)
2810 {
2811 InterpolateLocal1(field_in, gsl_elem_temp, gsl_ref_temp,
2812 interp_vals, nlocal, ncomp, dof1Dsol);
2813 }
2814 else if (dim == 2)
2815 {
2816 InterpolateLocal2(field_in, gsl_elem_temp, gsl_ref_temp,
2817 interp_vals, nlocal, ncomp, dof1Dsol);
2818
2819 }
2820#ifdef MFEM_USE_MPI
2821 MPI_Barrier(gsl_comm->c);
2822#endif
2823
2824 auto d_interp_vals = interp_vals.Read(use_dev);
2825 auto d_index_temp = index_temp.Read(use_dev);
2826 auto d_field_out = field_out.ReadWrite(use_dev); // no-op
2827 const int interp_offset = interp_vals.Size()/ncomp;
2828 InterpolateDeviceScatter(nlocal, d_index_temp, d_interp_vals,
2829 d_field_out, interp_offset, ncomp, points_cnt,
2830 field_out_ordering);
2831 }
2832#ifdef MFEM_USE_MPI
2833 MPI_Barrier(gsl_comm->c);
2834#endif
2835
2836 if (gsl_comm->np == 1)
2837 {
2838 array_free(&src);
2839 return;
2840 }
2841
2842 // evaluate points locally
2843 {
2844 int n = src.n;
2845 const evalSrcPt_t *spt;
2846 evalOutPt_t *opt;
2847 spt = (evalSrcPt_t *)src.ptr;
2848
2849 // Copy to host vector
2850 gsl_elem_temp.SetSize(n);
2851 gsl_elem_temp.HostWrite();
2852
2853 gsl_ref_temp.SetSize(n*dim);
2854 gsl_ref_temp.HostWrite();
2855
2856 spt = (evalSrcPt_t *)src.ptr;
2857 // opt = (evalOutPt_t *)outpt.ptr;
2858 for (int i=0; i<n; i++, ++spt)
2859 {
2860 gsl_elem_temp[i] = spt->el;
2861 for (int d=0; d<dim; d++)
2862 {
2863 gsl_ref_temp(i*dim + d) = spt->r[d];
2864 }
2865 }
2866
2867 Vector interp_vals(n*ncomp);
2868 interp_vals.UseDevice(use_dev);
2869 if (dim == 1)
2870 {
2871 InterpolateLocal1(field_in, gsl_elem_temp, gsl_ref_temp,
2872 interp_vals, n, ncomp, dof1Dsol);
2873 }
2874 else if (dim == 2)
2875 {
2876 InterpolateLocal2(field_in, gsl_elem_temp, gsl_ref_temp,
2877 interp_vals, n, ncomp, dof1Dsol);
2878 }
2879#ifdef MFEM_USE_MPI
2880 MPI_Barrier(gsl_comm->c);
2881#endif
2882 auto h_interp_vals = interp_vals.HostRead();
2883 auto h_field_out = field_out.HostReadWrite();
2884
2885 // Now the interpolated values need to be sent back component wise
2886 int Offset = interp_vals.Size()/ncomp;
2887 for (int i=0; i<ncomp; i++)
2888 {
2889 spt = (evalSrcPt_t *)src.ptr;
2890 array_init(evalOutPt_t, &outpt, n);
2891 outpt.n = n;
2892 opt = (evalOutPt_t *)outpt.ptr;
2893
2894 for (int j=0; j<n; j++)
2895 {
2896 opt->index = spt->index;
2897 opt->proc = spt->proc;
2898 opt->out = h_interp_vals[j + Offset*i];
2899 spt++;
2900 opt++;
2901 }
2902
2903 sarray_transfer(struct evalOutPt_t, &outpt, proc, 1, cr);
2904
2905 opt = (evalOutPt_t *)outpt.ptr;
2906 for (size_t index = 0; index < outpt.n; index++)
2907 {
2908 int idx = field_out_ordering == Ordering::byNODES ?
2909 opt->index + i*points_cnt :
2910 opt->index*ncomp + i;
2911 h_field_out[idx] = opt->out;
2912 ++opt;
2913 }
2914 array_free(&outpt);
2915 }
2916 array_free(&src);
2917 }
2918 //finished evaluating points received from other processors.
2919}
2920
2921#else
2923void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
2924 const int point_pos_ordering) {};
2925void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
2926 Vector &field_out,
2927 const int nel, const int ncomp,
2928 const int dof1dsol,
2929 const int ordering) {};
2930
2931void FindPointsGSLIB::FindPointsSurf(const Vector &point_pos,
2932 int point_pos_ordering)
2933{
2934 MFEM_ABORT("FindPointsGSLIB::FindPoints only supports surface meshes "
2935 "with GSLIB v1.0.9 or later.");
2936};
2937void FindPointsGSLIB::InterpolateSurfBase(const Vector &field_in_evec,
2938 Vector &field_out,
2939 const int nel, const int ncomp,
2940 const int dof1dsol,
2941 const int field_out_ordering) {};
2942#endif
2943
2944void FindPointsGSLIB::FindPoints(Mesh &m, const Vector &point_pos,
2945 const int point_pos_ordering,
2946 const double bbox_rel_size_inc,
2947 const double newt_tol, const int npt_max)
2948{
2949 if (!setupflag || (mesh != &m))
2950 {
2951 Setup(m, bbox_rel_size_inc, newt_tol, npt_max);
2952 }
2953 FindPoints(point_pos, point_pos_ordering);
2954}
2955
2957 const GridFunction &field_in,
2958 Vector &field_out,
2959 const int point_pos_ordering)
2960{
2961 FindPoints(point_pos, point_pos_ordering);
2962 Interpolate(field_in, field_out);
2963}
2964
2966 const GridFunction &field_in,
2967 Vector &field_out,
2968 const int point_pos_ordering,
2969 const int field_out_ordering)
2970{
2971 FindPoints(point_pos, point_pos_ordering);
2972 Interpolate(field_in, field_out, field_out_ordering);
2973}
2974
2975void FindPointsGSLIB::Interpolate(Mesh &m, const Vector &point_pos,
2976 const GridFunction &field_in,
2977 Vector &field_out,
2978 const int point_pos_ordering)
2979{
2980 FindPoints(m, point_pos, point_pos_ordering);
2981 Interpolate(field_in, field_out);
2982}
2983
2985{
2986 FreeCrystal();
2987 if (!setupflag) { return; }
2988 if (dim == spacedim)
2989 {
2990#ifdef MFEM_USE_MPI
2991 if (!Mpi::IsFinalized()) // currently segfaults inside gslib otherwise
2992#endif
2993 {
2994 if (dim == 2)
2995 {
2996 findpts_free_2((gslib::findpts_data_2 *)this->fdataD);
2997 }
2998 else
2999 {
3000 findpts_free_3((gslib::findpts_data_3 *)this->fdataD);
3001 }
3002 }
3003 }
3007 gsl_mesh.Destroy();
3008 gsl_ref.Destroy();
3009 gsl_dist.Destroy();
3010 for (int i = 0; i < 4; i++)
3011 {
3012 if (mesh_split[i]) { delete mesh_split[i]; mesh_split[i] = nullptr; }
3013 if (ir_split[i]) { delete ir_split[i]; ir_split[i] = nullptr; }
3014 if (ir_split_sol[i]) { delete ir_split_sol[i]; ir_split_sol[i] = nullptr; }
3015 if (fes_rst_map[i]) { delete fes_rst_map[i]; fes_rst_map[i] = nullptr; }
3016 if (gf_rst_map[i]) { delete gf_rst_map[i]; gf_rst_map[i] = nullptr; }
3017 }
3018 ir_split_sol_order = -1;
3019 if (fec_map_lin) { delete fec_map_lin; fec_map_lin = nullptr; }
3020 setupflag = false;
3021 DEV.setup_device = false;
3022 DEV.find_device = false;
3023 points_cnt = -1;
3024}
3025
3027{
3028 if (fec_map_lin == nullptr) { fec_map_lin = new H1_FECollection(1, dim); }
3029 if (mesh->Dimension() == 1)
3030 {
3032 }
3033 else if (mesh->Dimension() == 2)
3034 {
3035 int Nvert = 7;
3036 int NEsplit = 3;
3037 mesh_split[0] = new Mesh(2, Nvert, NEsplit, 0, 2);
3038
3039 const double quad_v[7][2] =
3040 {
3041 {0, 0}, {0.5, 0}, {1, 0}, {0, 0.5},
3042 {1./3., 1./3.}, {0.5, 0.5}, {0, 1}
3043 };
3044 const int quad_e[3][4] =
3045 {
3046 {0, 1, 4, 3}, {1, 2, 5, 4}, {3, 4, 5, 6}
3047 };
3048
3049 for (int j = 0; j < Nvert; j++)
3050 {
3051 mesh_split[0]->AddVertex(quad_v[j]);
3052 }
3053 for (int j = 0; j < NEsplit; j++)
3054 {
3055 int attribute = j + 1;
3056 mesh_split[0]->AddQuad(quad_e[j], attribute);
3057 }
3058 mesh_split[0]->FinalizeQuadMesh(1, 1, true);
3059
3061 gf_rst_map[0] = new GridFunction(fes_rst_map[0]);
3062 gf_rst_map[0]->UseDevice(false);
3063 const int npt = gf_rst_map[0]->Size()/dim;
3064 for (int k = 0; k < dim; k++)
3065 {
3066 for (int j = 0; j < npt; j++)
3067 {
3068 (*gf_rst_map[0])(j+k*npt) = quad_v[j][k];
3069 }
3070 }
3071
3074 }
3075 else if (dim == 3)
3076 {
3077 mesh_split[0] = new Mesh(Mesh::MakeCartesian3D(1, 1, 1,
3079 // Tetrahedron
3080 {
3081 int Nvert = 15;
3082 int NEsplit = 4;
3083 mesh_split[1] = new Mesh(3, Nvert, NEsplit, 0, 3);
3084
3085 const double hex_v[15][3] =
3086 {
3087 {0, 0, 0.}, {1, 0., 0.}, {0., 1., 0.}, {0, 0., 1.},
3088 {0.5, 0., 0.}, {0.5, 0.5, 0.}, {0., 0.5, 0.},
3089 {0., 0., 0.5}, {0.5, 0., 0.5}, {0., 0.5, 0.5},
3090 {1./3., 0., 1./3.}, {1./3., 1./3., 1./3.}, {0, 1./3., 1./3.},
3091 {1./3., 1./3., 0}, {0.25, 0.25, 0.25}
3092 };
3093 const int hex_e[4][8] =
3094 {
3095 {7, 10, 4, 0, 12, 14, 13, 6},
3096 {10, 8, 1, 4, 14, 11, 5, 13},
3097 {14, 11, 5, 13, 12, 9, 2, 6},
3098 {7, 3, 8, 10, 12, 9, 11, 14}
3099 };
3100
3101 for (int j = 0; j < Nvert; j++)
3102 {
3103 mesh_split[1]->AddVertex(hex_v[j]);
3104 }
3105 for (int j = 0; j < NEsplit; j++)
3106 {
3107 int attribute = j + 1;
3108 mesh_split[1]->AddHex(hex_e[j], attribute);
3109 }
3110 mesh_split[1]->FinalizeHexMesh(1, 1, true);
3111
3113 gf_rst_map[1] = new GridFunction(fes_rst_map[1]);
3114 gf_rst_map[1]->UseDevice(false);
3115 const int npt = gf_rst_map[1]->Size()/dim;
3116 for (int k = 0; k < dim; k++)
3117 {
3118 for (int j = 0; j < npt; j++)
3119 {
3120 (*gf_rst_map[1])(j+k*npt) = hex_v[j][k];
3121 }
3122 }
3123 }
3124 // Prism
3125 {
3126 int Nvert = 14;
3127 int NEsplit = 3;
3128 mesh_split[2] = new Mesh(3, Nvert, NEsplit, 0, 3);
3129
3130 const double hex_v[14][3] =
3131 {
3132 {0, 0, 0}, {0.5, 0, 0}, {1, 0, 0}, {0, 0.5, 0},
3133 {1./3., 1./3., 0}, {0.5, 0.5, 0}, {0, 1, 0},
3134 {0, 0, 1}, {0.5, 0, 1}, {1, 0, 1}, {0, 0.5, 1},
3135 {1./3., 1./3., 1}, {0.5, 0.5, 1}, {0, 1, 1}
3136 };
3137 const int hex_e[3][8] =
3138 {
3139 {0, 1, 4, 3, 7, 8, 11, 10},
3140 {1, 2, 5, 4, 8, 9, 12, 11},
3141 {3, 4, 5, 6, 10, 11, 12, 13}
3142 };
3143
3144 for (int j = 0; j < Nvert; j++)
3145 {
3146 mesh_split[2]->AddVertex(hex_v[j]);
3147 }
3148 for (int j = 0; j < NEsplit; j++)
3149 {
3150 int attribute = j + 1;
3151 mesh_split[2]->AddHex(hex_e[j], attribute);
3152 }
3153 mesh_split[2]->FinalizeHexMesh(1, 1, true);
3154
3156 gf_rst_map[2] = new GridFunction(fes_rst_map[2]);
3157 gf_rst_map[2]->UseDevice(false);
3158 const int npt = gf_rst_map[2]->Size()/dim;
3159 for (int k = 0; k < dim; k++)
3160 {
3161 for (int j = 0; j < npt; j++)
3162 {
3163 (*gf_rst_map[2])(j+k*npt) = hex_v[j][k];
3164 }
3165 }
3166 }
3167 // Pyramid
3168 {
3169 int Nvert = 23;
3170 int NEsplit = 8;
3171 mesh_split[3] = new Mesh(3, Nvert, NEsplit, 0, 3);
3172
3173 const double hex_v[23][3] =
3174 {
3175 {0.0000, 0.0000, 0.0000}, {0.5000, 0.0000, 0.0000},
3176 {0.0000, 0.0000, 0.5000}, {0.3333, 0.0000, 0.3333},
3177 {0.0000, 0.5000, 0.0000}, {0.3333, 0.3333, 0.0000},
3178 {0.0000, 0.3333, 0.3333}, {0.2500, 0.2500, 0.2500},
3179 {1.0000, 0.0000, 0.0000}, {0.5000, 0.0000, 0.5000},
3180 {0.5000, 0.5000, 0.0000}, {0.3333, 0.3333, 0.3333},
3181 {0.0000, 1.0000, 0.0000}, {0.0000, 0.5000, 0.5000},
3182 {0.0000, 0.0000, 1.0000}, {1.0000, 0.5000, 0.0000},
3183 {0.6667, 0.3333, 0.3333}, {0.6667, 0.6667, 0.0000},
3184 {0.5000, 0.5000, 0.2500}, {1.0000, 1.0000, 0.0000},
3185 {0.5000, 0.5000, 0.5000}, {0.5000, 1.0000, 0.0000},
3186 {0.3333, 0.6667, 0.3333}
3187 };
3188 const int hex_e[8][8] =
3189 {
3190 {2, 3, 1, 0, 6, 7, 5, 4}, {3, 9, 8, 1, 7, 11, 10, 5},
3191 {7, 11, 10, 5, 6, 13, 12, 4}, {2, 14, 9, 3, 6, 13, 11, 7},
3192 {9, 16, 15, 8, 11, 18, 17, 10}, {16, 20, 19, 15, 18, 22, 21, 17},
3193 {18, 22, 21, 17, 11, 13, 12, 10}, {9, 14, 20, 16, 11, 13, 22, 18}
3194 };
3195
3196 for (int j = 0; j < Nvert; j++)
3197 {
3198 mesh_split[3]->AddVertex(hex_v[j]);
3199 }
3200 for (int j = 0; j < NEsplit; j++)
3201 {
3202 int attribute = j + 1;
3203 mesh_split[3]->AddHex(hex_e[j], attribute);
3204 }
3205 mesh_split[3]->FinalizeHexMesh(1, 1, true);
3206
3208 gf_rst_map[3] = new GridFunction(fes_rst_map[3]);
3209 gf_rst_map[3]->UseDevice(false);
3210 const int npt = gf_rst_map[3]->Size()/dim;
3211 for (int k = 0; k < dim; k++)
3212 {
3213 for (int j = 0; j < npt; j++)
3214 {
3215 (*gf_rst_map[3])(j+k*npt) = hex_v[j][k];
3216 }
3217 }
3218 }
3219 }
3220}
3221
3223 IntegrationRule *irule,
3224 int order)
3225{
3226 H1_FECollection fec(order, dim);
3227 FiniteElementSpace nodal_fes(meshin, &fec, dim);
3228 GridFunction nodesplit(&nodal_fes);
3229 meshin->GetNodes(nodesplit);
3230 const int NEsplit = meshin->GetNE();
3231
3232 const int dof_cnt = nodal_fes.GetTypicalFE()->GetDof(),
3233 pts_cnt = NEsplit * dof_cnt;
3234 Vector irlist(dim * pts_cnt);
3235
3236 const TensorBasisElement *tbe =
3237 dynamic_cast<const TensorBasisElement *>(nodal_fes.GetTypicalFE());
3238 MFEM_VERIFY(tbe != nullptr, "TensorBasis FiniteElement expected.");
3239 const Array<int> &dof_map = tbe->GetDofMap();
3240
3241 DenseMatrix pos(dof_cnt, dim);
3242 Vector posV(pos.Data(), dof_cnt * dim);
3243 Array<int> xdofs(dof_cnt * dim);
3244
3245 // Create an IntegrationRule on the nodes of the reference submesh.
3246 MFEM_ASSERT(irule->GetNPoints() == pts_cnt, "IntegrationRule does not have"
3247 "the correct number of points.");
3248 int pt_id = 0;
3249 for (int i = 0; i < NEsplit; i++)
3250 {
3251 nodal_fes.GetElementVDofs(i, xdofs);
3252 nodesplit.GetSubVector(xdofs, posV);
3253 for (int j = 0; j < dof_cnt; j++)
3254 {
3255 for (int d = 0; d < dim; d++)
3256 {
3257 irlist(pts_cnt * d + pt_id) = pos(dof_map[j], d);
3258 }
3259 irule->IntPoint(pt_id).x = irlist(pt_id);
3260 if (dim >= 2)
3261 {
3262 irule->IntPoint(pt_id).y = irlist(pts_cnt + pt_id);
3263 }
3264 if (dim == 3)
3265 {
3266 irule->IntPoint(pt_id).z = irlist(2*pts_cnt + pt_id);
3267 }
3268 pt_id++;
3269 }
3270 }
3271}
3272
3275{
3276 MFEM_VERIFY(mesh, "Setup FindPointsGSLIB with mesh first.");
3277 const int dof1D = order+1;
3278 dim = mesh->Dimension();
3279
3280 if (dim == 1)
3281 {
3282 if (ir_out[0]) { delete ir_out[0]; ir_out[0] = nullptr; }
3283 ir_out[0] = new IntegrationRule(pow(dof1D, dim));
3284 SetupIntegrationRuleForSplitMesh(mesh_split[0], ir_out[0], order);
3285 }
3286 else if (dim == 2)
3287 {
3288 if (ir_out[0]) { delete ir_out[0]; ir_out[0] = nullptr; }
3289 ir_out[0] = new IntegrationRule(3*pow(dof1D, dim));
3290 SetupIntegrationRuleForSplitMesh(mesh_split[0], ir_out[0], order);
3291
3292 if (ir_out[1]) { delete ir_out[1]; ir_out[1] = nullptr; }
3293 ir_out[1] = new IntegrationRule(pow(dof1D, dim));
3294 SetupIntegrationRuleForSplitMesh(mesh_split[1], ir_out[1], order);
3295 }
3296 else if (dim == 3)
3297 {
3298 if (ir_out[0]) { delete ir_out[0]; ir_out[0] = nullptr; }
3299 ir_out[0] = new IntegrationRule(pow(dof1D, dim));
3300 SetupIntegrationRuleForSplitMesh(mesh_split[0], ir_out[0], order);
3301
3302 if (ir_out[1]) { delete ir_out[1]; ir_out[1] = nullptr; }
3303 ir_out[1] = new IntegrationRule(4*pow(dof1D, dim));
3304 SetupIntegrationRuleForSplitMesh(mesh_split[1], ir_out[1], order);
3305
3306 if (ir_out[2]) { delete ir_out[2]; ir_out[2] = nullptr; }
3307 ir_out[2] = new IntegrationRule(3*pow(dof1D, dim));
3308 SetupIntegrationRuleForSplitMesh(mesh_split[2], ir_out[2], order);
3309
3310 if (ir_out[3]) { delete ir_out[3]; ir_out[3] = nullptr; }
3311 ir_out[3] = new IntegrationRule(8*pow(dof1D, dim));
3312 SetupIntegrationRuleForSplitMesh(mesh_split[3], ir_out[3], order);
3313 }
3314}
3315
3317{
3318 MFEM_VERIFY(mesh, "Setup FindPointsGSLIB with mesh first.");
3319
3322
3323 // Setup map for non tensor-product elements
3324 NE_split_total = 0;
3328 int NEsplit = 0;
3329 for (int e = 0; e < mesh->GetNE(); e++)
3330 {
3332 if (gt == Geometry::TRIANGLE || gt == Geometry::PRISM)
3333 {
3334 NEsplit = 3;
3335 }
3336 else if (gt == Geometry::TETRAHEDRON)
3337 {
3338 NEsplit = 4;
3339 }
3340 else if (gt == Geometry::PYRAMID)
3341 {
3342 NEsplit = 8;
3343 }
3344
3345 else if (gt == Geometry::SEGMENT ||
3346 gt == Geometry::SQUARE || gt == Geometry::CUBE)
3347 {
3348 NEsplit = 1;
3349 }
3350 else
3351 {
3352 MFEM_ABORT("Unsupported geometry type.");
3353 }
3354 NE_split_total += NEsplit;
3355 for (int i = 0; i < NEsplit; i++)
3356 {
3359 split_element_geom.Append((int)gt);
3360 }
3361 }
3362}
3363
3365 Vector &node_vals,
3366 const Array<IntegrationRule *> *ir_in,
3367 bool by_element) const
3368{
3369 const GridFunction *nodes = gf_in;
3370 const FiniteElementSpace *fes = nodes->FESpace();
3371 const int NE = mesh->GetNE();
3372 const int vdim = fes->GetVDim();
3373
3374 const Array<IntegrationRule *> &ir = ir_in ? *ir_in : ir_split;
3375 IntegrationRule *ir_split_temp = nullptr;
3376
3377 const int maxOrder = fes->GetMaxElementOrder();
3378 const int dof_1D = maxOrder+1;
3379 const int pts_el = std::pow(dof_1D, dim);
3380 const int pts_cnt = NE_split_total * pts_el;
3381 node_vals.SetSize(vdim * pts_cnt);
3382
3383 if (node_vals.UseDevice())
3384 {
3385 node_vals.HostWrite();
3386 }
3387
3388 int gsl_mesh_pt_index = 0;
3389
3390 for (int e = 0; e < NE; e++)
3391 {
3392 const FiniteElement *fe = fes->GetFE(e);
3393 const Geometry::Type gt = fe->GetGeomType();
3394 bool el_to_split = true;
3395 if (gt == Geometry::TRIANGLE)
3396 {
3397 ir_split_temp = ir[0];
3398 }
3399 else if (gt == Geometry::TETRAHEDRON)
3400 {
3401 ir_split_temp = ir[1];
3402 }
3403 else if (gt == Geometry::PRISM)
3404 {
3405 ir_split_temp = ir[2];
3406 }
3407 else if (gt == Geometry::PYRAMID)
3408 {
3409 ir_split_temp = ir[3];
3410 }
3411 else if (gt == Geometry::SQUARE)
3412 {
3413 ir_split_temp = ir[1];
3414 // "split" if input mesh is not a tensor basis or has mixed order
3415 el_to_split =
3416 gf_in->FESpace()->IsVariableOrder() ||
3417 dynamic_cast<const TensorBasisElement *>(fes->GetFE(e)) == nullptr;
3418 }
3419 else if (gt == Geometry::SEGMENT || gt == Geometry::CUBE)
3420 {
3421 ir_split_temp = ir[0];
3422 // "split" if input mesh is not a tensor basis or has mixed order
3423 el_to_split =
3424 gf_in->FESpace()->IsVariableOrder() ||
3425 dynamic_cast<const TensorBasisElement *>(fes->GetFE(e)) == nullptr;
3426 }
3427 else
3428 {
3429 MFEM_ABORT("Unsupported geometry type.");
3430 }
3431
3432 if (el_to_split) // Triangle/Tet/Prism or Quads/Hex but variable order
3433 {
3434 // Fill node_vals with field values at split points.
3435 Vector locval(vdim);
3436 for (int i = 0; i < ir_split_temp->GetNPoints(); i++)
3437 {
3438 const IntegrationPoint &ip = ir_split_temp->IntPoint(i);
3439 nodes->GetVectorValue(e, ip, locval);
3440 if (by_element)
3441 {
3442 const int ei = gsl_mesh_pt_index / pts_el;
3443 const int pi = gsl_mesh_pt_index % pts_el;
3444 for (int d = 0; d < vdim; d++)
3445 {
3446 node_vals(ei * pts_el * vdim + d * pts_el + pi) = locval(d);
3447 }
3448 }
3449 else
3450 {
3451 for (int d = 0; d < vdim; d++)
3452 {
3453 node_vals(pts_cnt*d + gsl_mesh_pt_index) = locval(d);
3454 }
3455 }
3456 gsl_mesh_pt_index++;
3457 }
3458 }
3459 else // Quad/Hex and constant polynomial order
3460 {
3461 const int dof_cnt_split = fe->GetDof();
3462
3463 const TensorBasisElement *tbe =
3464 dynamic_cast<const TensorBasisElement *>(fes->GetFE(e));
3465 MFEM_VERIFY(tbe != nullptr, "TensorBasis FiniteElement expected.");
3466 Array<int> dof_map(dof_cnt_split);
3467 const Array<int> &dm = tbe->GetDofMap();
3468 if (dm.Size() > 0) { dof_map = dm; }
3469 else { for (int i = 0; i < dof_cnt_split; i++) { dof_map[i] = i; } }
3470
3471 DenseMatrix pos(dof_cnt_split, vdim);
3472 Vector posV(pos.Data(), dof_cnt_split * vdim);
3473 Array<int> xdofs(dof_cnt_split * vdim);
3474
3475 fes->GetElementVDofs(e, xdofs);
3476 nodes->GetSubVector(xdofs, posV);
3477 for (int j = 0; j < dof_cnt_split; j++)
3478 {
3479 if (by_element)
3480 {
3481 const int ei = gsl_mesh_pt_index / pts_el;
3482 const int pi = gsl_mesh_pt_index % pts_el;
3483 for (int d = 0; d < vdim; d++)
3484 {
3485 node_vals(ei * pts_el * vdim + d * pts_el + pi) =
3486 pos(dof_map[j], d);
3487 }
3488 }
3489 else
3490 {
3491 for (int d = 0; d < vdim; d++)
3492 {
3493 node_vals(pts_cnt * d + gsl_mesh_pt_index) = pos(dof_map[j], d);
3494 }
3495 }
3496 gsl_mesh_pt_index++;
3497 }
3498 }
3499 }
3500}
3501
3503{
3508
3509 gsl_mfem_ref += 1.; // map [-1, 1] to [0, 2] to [0, 1]
3510 gsl_mfem_ref *= 0.5;
3511
3512 int nptorig = points_cnt,
3513 npt = points_cnt;
3514
3515 // Tolerance for point to be marked as on element edge/face based on the
3516 // obtained reference-space coordinates.
3517 double rbtol = 1e-12;
3518
3519 GridFunction *gf_rst_map_temp = nullptr;
3520 int nptsend = 0;
3521
3522 for (int index = 0; index < npt; index++)
3523 {
3524 if (gsl_code[index] != 2 && gsl_proc[index] != gsl_comm->id)
3525 {
3526 nptsend +=1;
3527 }
3528 }
3529
3530 // Pack data to send via crystal router
3531 struct gslib::array *outpt = new gslib::array;
3532 struct out_pt { double r[3]; uint index, el, proc, code; };
3533 struct out_pt *pt;
3534 array_init(struct out_pt, outpt, nptsend);
3535 outpt->n=nptsend;
3536 pt = (struct out_pt *)outpt->ptr;
3537 for (int index = 0; index < npt; index++)
3538 {
3539 if (gsl_code[index] == 2 || gsl_proc[index] == gsl_comm->id)
3540 {
3541 continue;
3542 }
3543 for (int d = 0; d < dim; ++d)
3544 {
3545 pt->r[d]= gsl_mfem_ref(index*dim + d);
3546 }
3547 pt->index = index;
3548 pt->proc = gsl_proc[index];
3549 pt->el = gsl_elem[index];
3550 pt->code = gsl_code[index];
3551 ++pt;
3552 }
3553
3554 // Transfer data to target MPI ranks
3555 sarray_transfer(struct out_pt, outpt, proc, 1, cr);
3556
3557 // Map received points
3558 npt = outpt->n;
3559 pt = (struct out_pt *)outpt->ptr;
3560 for (int index = 0; index < npt; index++)
3561 {
3563 ip.Set3(&pt->r[0]);
3564 const int elem = pt->el;
3565 const int mesh_elem = split_element_map[elem];
3566 const FiniteElement *fe = mesh->GetNodalFESpace()->GetFE(mesh_elem);
3567
3568 const Geometry::Type gt = fe->GetGeomType();
3569 pt->el = mesh_elem;
3570
3571 if (gt == Geometry::SQUARE || gt == Geometry::CUBE)
3572 {
3573 // check if it is on element boundary
3574 pt->code = Geometry::CheckPoint(gt, ip, -rbtol) ? 0 : 1;
3575 ++pt;
3576 continue;
3577 }
3578 else if (gt == Geometry::TRIANGLE)
3579 {
3580 gf_rst_map_temp = gf_rst_map[0];
3581 }
3582 else if (gt == Geometry::TETRAHEDRON)
3583 {
3584 gf_rst_map_temp = gf_rst_map[1];
3585 }
3586 else if (gt == Geometry::PRISM)
3587 {
3588 gf_rst_map_temp = gf_rst_map[2];
3589 }
3590 else if (gt == Geometry::PYRAMID)
3591 {
3592 gf_rst_map_temp = gf_rst_map[3];
3593 }
3594
3595 int local_elem = split_element_index[elem];
3596 Vector mfem_ref(dim);
3597 // map to rst of macro element
3598 gf_rst_map_temp->GetVectorValue(local_elem, ip, mfem_ref);
3599
3600 for (int d = 0; d < dim; d++)
3601 {
3602 pt->r[d] = mfem_ref(d);
3603 }
3604
3605 // check if point is on element boundary
3606 ip.Set3(&pt->r[0]);
3607 pt->code = Geometry::CheckPoint(gt, ip, -rbtol) ? 0 : 1;
3608 ++pt;
3609 }
3610
3611 // Transfer data back to source MPI rank
3612 sarray_transfer(struct out_pt, outpt, proc, 1, cr);
3613 npt = outpt->n;
3614
3615 // First copy mapped information for points on other procs
3616 pt = (struct out_pt *)outpt->ptr;
3617 for (int index = 0; index < npt; index++)
3618 {
3619 gsl_mfem_elem[pt->index] = pt->el;
3620 for (int d = 0; d < dim; d++)
3621 {
3622 gsl_mfem_ref(d + pt->index*dim) = pt->r[d];
3623 }
3624 gsl_code[pt->index] = pt->code;
3625 ++pt;
3626 }
3627 array_free(outpt);
3628 delete outpt;
3629
3630 // Now map information for points on the same proc
3631 for (int index = 0; index < nptorig; index++)
3632 {
3633 if (gsl_code[index] != 2 && gsl_proc[index] == gsl_comm->id)
3634 {
3635
3637 Vector mfem_ref(gsl_mfem_ref.GetData()+index*dim, dim);
3638 ip.Set2(mfem_ref.GetData());
3639 if (dim == 3) { ip.z = mfem_ref(2); }
3640
3641 const int elem = gsl_elem[index];
3642 const int mesh_elem = split_element_map[elem];
3643 const FiniteElement *fe = mesh->GetNodalFESpace()->GetFE(mesh_elem);
3644 const Geometry::Type gt = fe->GetGeomType();
3645 gsl_mfem_elem[index] = mesh_elem;
3646 if (gt == Geometry::SQUARE || gt == Geometry::CUBE)
3647 {
3648 gsl_code[index] = Geometry::CheckPoint(gt, ip, -rbtol) ? 0 : 1;
3649 continue;
3650 }
3651 else if (gt == Geometry::TRIANGLE)
3652 {
3653 gf_rst_map_temp = gf_rst_map[0];
3654 }
3655 else if (gt == Geometry::TETRAHEDRON)
3656 {
3657 gf_rst_map_temp = gf_rst_map[1];
3658 }
3659 else if (gt == Geometry::PRISM)
3660 {
3661 gf_rst_map_temp = gf_rst_map[2];
3662 }
3663 else if (gt == Geometry::PYRAMID)
3664 {
3665 gf_rst_map_temp = gf_rst_map[3];
3666 }
3667
3668 int local_elem = split_element_index[elem];
3669 gf_rst_map_temp->GetVectorValue(local_elem, ip, mfem_ref);
3670
3671 // Check if the point is on element boundary
3672 ip.Set2(mfem_ref.GetData());
3673 if (dim == 3) { ip.z = mfem_ref(2); }
3674 gsl_code[index] = Geometry::CheckPoint(gt, ip, -rbtol) ? 0 : 1;
3675 }
3676 }
3677}
3678
3680 Vector &field_out)
3681{
3682 Interpolate(field_in, field_out, field_in.FESpace()->GetOrdering());
3683}
3684
3686 Vector &field_out,
3687 const int field_out_ordering)
3688{
3689 MFEM_VERIFY(setupflag, "FindPointsGSLIB::Setup must be called first.");
3690 if (dim != spacedim)
3691 {
3692 InterpolateSurf(field_in, field_out, field_out_ordering);
3693 return;
3694 }
3695 const int gf_order = field_in.FESpace()->GetMaxElementOrder(),
3696 mesh_order = mesh->GetNodalFESpace()->GetMaxElementOrder();
3697
3698 const FiniteElementCollection *fec_in = field_in.FESpace()->FEColl();
3699 const H1_FECollection *fec_h1 = dynamic_cast<const H1_FECollection *>(fec_in);
3700 const L2_FECollection *fec_l2 = dynamic_cast<const L2_FECollection *>(fec_in);
3701
3702 bool tensor_product_only = mesh->GetNE() == 0 ||
3703 (mesh->GetNumGeometries(dim) == 1 &&
3706#ifdef MFEM_USE_MPI
3707 MPI_Allreduce(MPI_IN_PLACE, &tensor_product_only, 1, MFEM_MPI_CXX_BOOL,
3708 MPI_LAND, gsl_comm->c);
3709#endif
3710
3711 bool field_in_on_dev = Device::IsEnabled() && field_in.UseDevice();
3712 bool field_out_on_dev = Device::IsEnabled() && field_out.UseDevice();
3713
3714 if (field_in_on_dev && fec_h1 &&
3715 !field_in.FESpace()->IsVariableOrder() && tensor_product_only)
3716 {
3717#if GSLIB_RELEASE_VERSION == 10007
3719 {
3720 MFEM_ABORT("Either update to gslib v1.0.9 for GPU support "
3721 "or use SetGPUtoCPUFallback to use host-functions. See "
3722 "INSTALL for instructions to update GSLIB");
3723 }
3724#else
3725 MFEM_VERIFY(fec_h1->GetBasisType() == BasisType::GaussLobatto,
3726 "basis not supported");
3727 Vector node_vals;
3729 const Operator *R = field_in.FESpace()->GetElementRestriction(ordering);
3730 node_vals.UseDevice(true);
3731 node_vals.SetSize(R->Height(), Device::GetMemoryType());
3732 R->Mult(field_in, node_vals);
3733 // GetNodalValues(&field_in, node_vals);
3734
3735 const int ncomp = field_in.FESpace()->GetVDim();
3736 const int maxOrder = field_in.FESpace()->GetMaxElementOrder();
3737
3738 InterpolateOnDevice(node_vals, field_out, NE_split_total, ncomp,
3739 maxOrder+1, field_out_ordering);
3740 return;
3741#endif
3742 }
3743 // The remaining interpolation paths are host-only. They access field_out
3744 // through explicit host pointers and address device validity before
3745 // returning (if field_out or field_in is on device).
3746 field_in.HostRead();
3747
3748 // Read GSLIB data to host in case it was done on device.
3749 auto h_gsl_code = gsl_code.HostRead();
3752 gsl_ref.HostRead();
3755
3756 if (fec_h1 && gf_order == mesh_order &&
3758 field_in.FESpace()->IsVariableOrder() ==
3760 {
3761 InterpolateH1(field_in, field_out, field_out_ordering);
3762 if (field_in_on_dev || field_out_on_dev)
3763 {
3764 field_out.ReadWrite(); // also internally calls mem.UseDevice(true);
3765 }
3766 return;
3767 }
3768 else
3769 {
3770 InterpolateGeneral(field_in, field_out, field_out_ordering);
3771 if (!fec_l2 || avgtype == AvgType::NONE)
3772 {
3773 if (field_in_on_dev || field_out_on_dev)
3774 {
3775 field_out.ReadWrite(); // also internally calls mem.UseDevice(true);
3776 }
3777 return;
3778 }
3779 }
3780
3781 // For points on element borders, project the L2 GridFunction to H1 and
3782 // re-interpolate.
3783 if (fec_l2)
3784 {
3785 Array<int> indl2;
3786 for (int i = 0; i < points_cnt; i++)
3787 {
3788 if (h_gsl_code[i] == 1) { indl2.Append(i); }
3789 }
3790 int borderPts = indl2.Size();
3791#ifdef MFEM_USE_MPI
3792 MPI_Allreduce(MPI_IN_PLACE, &borderPts, 1, MPI_INT, MPI_SUM, gsl_comm->c);
3793#endif
3794 if (borderPts == 0)
3795 {
3796 if (field_in_on_dev || field_out_on_dev)
3797 {
3798 field_out.ReadWrite(); // also internally calls mem.UseDevice(true);
3799 }
3800 return; // no points on element borders
3801 }
3802
3803 Vector field_out_l2(field_out.Size());
3804 VectorGridFunctionCoefficient field_in_dg(&field_in);
3805 int gf_order_h1 = std::max(gf_order, 1); // H1 should be at least order 1
3806 H1_FECollection fec(gf_order_h1, dim);
3807 const int ncomp = field_in.FESpace()->GetVDim();
3808
3809 std::unique_ptr<FiniteElementSpace> fes;
3810 std::unique_ptr<GridFunction> field_in_h1;
3811#ifdef MFEM_USE_MPI
3812 if (auto *pmesh = dynamic_cast<ParMesh*>(mesh))
3813 {
3814 fes = std::make_unique<ParFiniteElementSpace>(
3815 pmesh, &fec, ncomp, field_in.FESpace()->GetOrdering());
3816
3817 field_in_h1 = std::make_unique<ParGridFunction>(
3818 static_cast<ParFiniteElementSpace*>(fes.get()));
3819 }
3820 else
3821#endif
3822 {
3823 fes = std::make_unique<FiniteElementSpace>(
3824 mesh, &fec, ncomp, field_in.FESpace()->GetOrdering());
3825
3826 field_in_h1 = std::make_unique<GridFunction>(fes.get());
3827 }
3828
3829 field_in_h1->UseDevice(false);
3830
3832 {
3833 field_in_h1->ProjectDiscCoefficient(field_in_dg, GridFunction::ARITHMETIC);
3834 }
3835 else if (avgtype == AvgType::HARMONIC)
3836 {
3837 field_in_h1->ProjectDiscCoefficient(field_in_dg, GridFunction::HARMONIC);
3838 }
3839 else
3840 {
3841 MFEM_ABORT("Invalid averaging type.");
3842 }
3843
3844 if (gf_order_h1 == mesh_order) // basis is GaussLobatto by default
3845 {
3846 InterpolateH1(*field_in_h1, field_out_l2, field_out_ordering);
3847 }
3848 else
3849 {
3850 InterpolateGeneral(*field_in_h1, field_out_l2, field_out_ordering);
3851 }
3852
3853 // Copy interpolated values for the points on element border
3854 real_t *h_field_out = field_out.HostReadWrite();
3855 const real_t *h_field_out_l2 = field_out_l2.HostRead();
3856 for (int j = 0; j < ncomp; j++)
3857 {
3858 for (int i = 0; i < indl2.Size(); i++)
3859 {
3860 int idx = field_out_ordering == Ordering::byNODES?
3861 indl2[i] + j*points_cnt:
3862 indl2[i]*ncomp + j;
3863 h_field_out[idx] = h_field_out_l2[idx];
3864 }
3865 }
3866 if (field_in_on_dev || field_out_on_dev)
3867 {
3868 field_out.ReadWrite(); // also internally calls mem.UseDevice(true);
3869 }
3870 }
3871}
3872
3874 Vector &field_out)
3875{
3876 InterpolateSurf(field_in, field_out, field_in.FESpace()->GetOrdering());
3877}
3878
3880 Vector &field_out,
3881 const int field_out_ordering)
3882{
3883 MFEM_VERIFY(setupflag, "FindPointsGSLIB::Setup must be called first.");
3884#if GSLIB_RELEASE_VERSION < 10009
3885 MFEM_ABORT("Update to gslib v1.0.9 for surface mesh support.");
3886#endif
3887 const FiniteElementCollection *fec_in = field_in.FESpace()->FEColl();
3888 const H1_FECollection *fec_h1 = dynamic_cast<const H1_FECollection *>
3889 (fec_in);
3890 MFEM_VERIFY(fec_h1,"Only h1 functions supported for surface meshes.");
3891 MFEM_VERIFY(fec_h1->GetBasisType() == BasisType::GaussLobatto,
3892 "basis not supported");
3893 MFEM_VERIFY(dim < spacedim, "InterpolateSurf is only for surface meshes.");
3894
3895 bool has_split_elems = false;
3896 bool supported_surf_elem = true;
3897 for (int e = 0; e < mesh->GetNE(); e++)
3898 {
3899 const Element::Type t = mesh->GetElementType(e);
3900 if (t == Element::TRIANGLE) { has_split_elems = true; }
3901 else if (t != Element::SEGMENT && t != Element::QUADRILATERAL)
3902 {
3903 supported_surf_elem = false;
3904 }
3905 }
3906#ifdef MFEM_USE_MPI
3907 MPI_Allreduce(MPI_IN_PLACE, &supported_surf_elem, 1, MFEM_MPI_CXX_BOOL,
3908 MPI_LAND, gsl_comm->c);
3909#endif
3910 MFEM_VERIFY(supported_surf_elem,
3911 "FindPointsGSLIB surface mesh support: only SEGMENT, "
3912 "QUADRILATERAL, and TRIANGLE elements are supported.");
3913 MFEM_VERIFY(dim < spacedim, "InterpolateSurf only supports surface meshes.");
3914
3915 bool use_dev = field_in.UseDevice();
3916
3917 if (!field_in.FESpace()->IsVariableOrder())
3918 {
3919 Vector node_vals;
3920
3921 if (has_split_elems)
3922 {
3923 const int fieldOrder = field_in.FESpace()->GetMaxElementOrder();
3924 const int meshOrder = DEV.dof1d - 1;
3925
3926 const Array<IntegrationRule *> *ir_to_use;
3927 if (fieldOrder == meshOrder)
3928 {
3929 ir_to_use = &ir_split;
3930 }
3931 else
3932 {
3933 if (fieldOrder != ir_split_sol_order)
3934 {
3936 ir_split_sol_order = fieldOrder;
3937 }
3938 ir_to_use = &ir_split_sol;
3939 }
3940 node_vals.UseDevice(use_dev);
3941 GetNodalValues(&field_in, node_vals, ir_to_use, true);
3942 }
3943 else
3944 {
3945 node_vals.UseDevice(use_dev);
3947 const Operator *R = field_in.FESpace()->GetElementRestriction(ord);
3948 node_vals.SetSize(R->Height());
3949 R->Mult(field_in, node_vals); // layout: [NEL][VDIM][N^D]
3950 }
3951
3952 const int ncomp = field_in.FESpace()->GetVDim();
3953 const int maxOrder = field_in.FESpace()->GetMaxElementOrder();
3954 DEV.dof1d_sol = maxOrder+1;
3955 DEV.gll1d_sol.UseDevice(use_dev);
3957 DEV.lagcoeff_sol.UseDevice(use_dev);
3959 if (DEV.dof1d_sol != DEV.dof1d)
3960 {
3961 auto h_gll1d_sol = DEV.gll1d_sol.HostWrite();
3962 auto h_lagcoeff_sol = DEV.lagcoeff_sol.HostWrite();
3963 gslib::lobatto_nodes(h_gll1d_sol, DEV.dof1d_sol);
3964 gslib::gll_lag_setup(h_lagcoeff_sol, DEV.dof1d_sol);
3965 }
3966 else
3967 {
3968 auto h_gll1d_sol = DEV.gll1d_sol.HostWrite();
3969 auto h_lagcoeff_sol = DEV.lagcoeff_sol.HostWrite();
3970 auto h_gll1d = DEV.gll1d.HostRead();
3971 auto h_lagcoeff = DEV.lagcoeff.HostRead();
3972 for (int i = 0; i < DEV.dof1d_sol; i++)
3973 {
3974 h_gll1d_sol[i] = h_gll1d[i];
3975 h_lagcoeff_sol[i] = h_lagcoeff[i];
3976 }
3977 }
3978
3979 field_out.SetSize(points_cnt*ncomp);
3980 field_out.UseDevice(use_dev);
3981 field_out = default_interp_value;
3982
3983 InterpolateSurfBase(node_vals, field_out, NE_split_total, ncomp,
3984 DEV.dof1d_sol, field_out_ordering);
3985 return;
3986 }
3987 else
3988 {
3989 MFEM_ABORT("Variable order functions not supported for surface meshes!!");
3990 }
3991}
3992
3994 Vector &field_out,
3995 const int field_out_ordering)
3996{
3997 FiniteElementSpace ind_fes(mesh, field_in.FESpace()->FEColl());
3998 if (field_in.FESpace()->IsVariableOrder())
3999 {
4000 for (int e = 0; e < ind_fes.GetMesh()->GetNE(); e++)
4001 {
4002 ind_fes.SetElementOrder(e, field_in.FESpace()->GetElementOrder(e));
4003 }
4004 ind_fes.Update(false);
4005 }
4006 GridFunction field_in_scalar(&ind_fes);
4007 field_in_scalar.UseDevice(false);
4008 Vector node_vals;
4009
4010 const int ncomp = field_in.FESpace()->GetVDim(),
4011 points_fld = field_in.Size() / ncomp;
4012 MFEM_VERIFY(points_cnt == gsl_code.Size(),
4013 "FindPointsGSLIB::InterpolateH1: Inconsistent size of gsl_code");
4014
4015 field_out.SetSize(points_cnt*ncomp);
4016 real_t *h_field_out = field_out.HostWrite();
4017 std::fill(h_field_out, h_field_out + field_out.Size(),
4019
4020 for (int i = 0; i < ncomp; i++)
4021 {
4022 const int dataptrin = i*points_fld,
4023 dataptrout = i*points_cnt;
4024 if (field_in.FESpace()->GetOrdering() == Ordering::byNODES)
4025 {
4026 field_in_scalar.NewDataAndSize(field_in.GetData()+dataptrin,
4027 points_fld);
4028 }
4029 else
4030 {
4031 for (int j = 0; j < points_fld; j++)
4032 {
4033 field_in_scalar(j) = field_in(i + j*ncomp);
4034 }
4035 }
4036 GetNodalValues(&field_in_scalar, node_vals);
4037
4038 if (dim==2)
4039 {
4040 findpts_eval_2(h_field_out+dataptrout, sizeof(double),
4041 gsl_code.GetData(), sizeof(unsigned int),
4042 gsl_proc.GetData(), sizeof(unsigned int),
4043 gsl_elem.GetData(), sizeof(unsigned int),
4044 gsl_ref.GetData(), sizeof(double) * dim,
4045 points_cnt, node_vals.GetData(),
4046 (gslib::findpts_data_2 *)this->fdataD);
4047 }
4048 else
4049 {
4050 findpts_eval_3(h_field_out+dataptrout, sizeof(double),
4051 gsl_code.GetData(), sizeof(unsigned int),
4052 gsl_proc.GetData(), sizeof(unsigned int),
4053 gsl_elem.GetData(), sizeof(unsigned int),
4054 gsl_ref.GetData(), sizeof(double) * dim,
4055 points_cnt, node_vals.GetData(),
4056 (gslib::findpts_data_3 *)this->fdataD);
4057 }
4058 }
4059 if (field_out_ordering == Ordering::byVDIM)
4060 {
4061 Vector field_out_temp(field_out.Size()); // host-only
4062 field_out_temp = h_field_out;
4063 for (int i = 0; i < ncomp; i++)
4064 {
4065 for (int j = 0; j < points_cnt; j++)
4066 {
4067 h_field_out[i + j*ncomp] = field_out_temp(j + i*points_cnt);
4068 }
4069 }
4070 }
4071}
4072
4074 Vector &field_out,
4075 const int field_out_ordering)
4076{
4077 int ncomp = field_in.VectorDim(),
4078 nptorig = points_cnt,
4079 npt = points_cnt;
4080
4081 field_out.SetSize(points_cnt*ncomp);
4082 real_t *h_field_out = field_out.HostWrite();
4083 std::fill(h_field_out, h_field_out + field_out.Size(),
4085
4086 // Get host read pointers in case FindPoints was done on device.
4087 auto h_gsl_code = gsl_code.HostRead();
4088 auto h_gsl_mfem_ref = gsl_mfem_ref.HostRead();
4089 auto h_gsl_mfem_elem = gsl_mfem_elem.HostRead();
4090 auto h_gsl_proc = gsl_proc.HostRead();
4091
4092 if (gsl_comm->np == 1) // serial
4093 {
4094 for (int index = 0; index < npt; index++)
4095 {
4096 if (h_gsl_code[index] == 2) { continue; }
4098 ip.Set(h_gsl_mfem_ref + index*dim, dim);
4099 Vector localval(ncomp);
4100 field_in.GetVectorValue(h_gsl_mfem_elem[index], ip, localval);
4101 if (field_out_ordering == Ordering::byNODES)
4102 {
4103 for (int i = 0; i < ncomp; i++)
4104 {
4105 h_field_out[index + i*npt] = localval(i);
4106 }
4107 }
4108 else //byVDIM
4109 {
4110 for (int i = 0; i < ncomp; i++)
4111 {
4112 h_field_out[index*ncomp + i] = localval(i);
4113 }
4114 }
4115 }
4116 }
4117 else // parallel
4118 {
4119 // Determine number of points to be sent
4120 int nptsend = 0;
4121 for (int index = 0; index < npt; index++)
4122 {
4123 if (h_gsl_code[index] != 2) { nptsend +=1; }
4124 }
4125
4126 // Pack data to send via crystal router
4127 struct gslib::array *outpt = new gslib::array;
4128 struct out_pt { double r[3], ival; uint index, el, proc; };
4129 struct out_pt *pt;
4130 array_init(struct out_pt, outpt, nptsend);
4131 outpt->n=nptsend;
4132 pt = (struct out_pt *)outpt->ptr;
4133 for (int index = 0; index < npt; index++)
4134 {
4135 if (h_gsl_code[index] == 2) { continue; }
4136 for (int d = 0; d < dim; ++d)
4137 {
4138 pt->r[d]= h_gsl_mfem_ref[index*dim + d];
4139 }
4140 pt->index = index;
4141 pt->proc = h_gsl_proc[index];
4142 pt->el = h_gsl_mfem_elem[index];
4143 ++pt;
4144 }
4145
4146 // Transfer data to target MPI ranks
4147 sarray_transfer(struct out_pt, outpt, proc, 1, cr);
4148
4149 if (ncomp == 1)
4150 {
4151 // Interpolate the grid function
4152 npt = outpt->n;
4153 pt = (struct out_pt *)outpt->ptr;
4154 for (int index = 0; index < npt; index++)
4155 {
4157 ip.Set3(&pt->r[0]);
4158 pt->ival = field_in.GetValue(pt->el, ip, 1);
4159 ++pt;
4160 }
4161
4162 // Transfer data back to source MPI rank
4163 sarray_transfer(struct out_pt, outpt, proc, 1, cr);
4164 npt = outpt->n;
4165 pt = (struct out_pt *)outpt->ptr;
4166 for (int index = 0; index < npt; index++)
4167 {
4168 h_field_out[pt->index] = pt->ival;
4169 ++pt;
4170 }
4171 array_free(outpt);
4172 delete outpt;
4173 }
4174 else // ncomp > 1
4175 {
4176 // Interpolate data and store in a Vector
4177 npt = outpt->n;
4178 pt = (struct out_pt *)outpt->ptr;
4179 Vector vec_int_vals(npt*ncomp);
4180 for (int index = 0; index < npt; index++)
4181 {
4183 ip.Set3(&pt->r[0]);
4184 Vector localval(vec_int_vals.GetData()+index*ncomp, ncomp);
4185 field_in.GetVectorValue(pt->el, ip, localval);
4186 ++pt;
4187 }
4188
4189 // Save index and proc data in a struct
4190 struct gslib::array *savpt = new gslib::array;
4191 struct sav_pt { uint index, proc; };
4192 struct sav_pt *spt;
4193 array_init(struct sav_pt, savpt, npt);
4194 savpt->n=npt;
4195 spt = (struct sav_pt *)savpt->ptr;
4196 pt = (struct out_pt *)outpt->ptr;
4197 for (int index = 0; index < npt; index++)
4198 {
4199 spt->index = pt->index;
4200 spt->proc = pt->proc;
4201 ++pt; ++spt;
4202 }
4203
4204 array_free(outpt);
4205 delete outpt;
4206
4207 // Copy data from save struct to send struct and send component wise
4208 struct gslib::array *sendpt = new gslib::array;
4209 struct send_pt { double ival; uint index, proc; };
4210 struct send_pt *sdpt;
4211 for (int j = 0; j < ncomp; j++)
4212 {
4213 array_init(struct send_pt, sendpt, npt);
4214 sendpt->n=npt;
4215 spt = (struct sav_pt *)savpt->ptr;
4216 sdpt = (struct send_pt *)sendpt->ptr;
4217 for (int index = 0; index < npt; index++)
4218 {
4219 sdpt->index = spt->index;
4220 sdpt->proc = spt->proc;
4221 sdpt->ival = vec_int_vals(j + index*ncomp);
4222 ++sdpt; ++spt;
4223 }
4224
4225 sarray_transfer(struct send_pt, sendpt, proc, 1, cr);
4226 sdpt = (struct send_pt *)sendpt->ptr;
4227 for (int index = 0; index < static_cast<int>(sendpt->n); index++)
4228 {
4229 int idx = field_out_ordering == Ordering::byNODES ?
4230 sdpt->index + j*nptorig :
4231 sdpt->index*ncomp + j;
4232 h_field_out[idx] = sdpt->ival;
4233 ++sdpt;
4234 }
4235 array_free(sendpt);
4236 }
4237 array_free(savpt);
4238 delete sendpt;
4239 delete savpt;
4240 } // ncomp > 1
4241 } // parallel
4242}
4243
4245{
4246 Array<unsigned int> nf_idxs;
4247 auto h_gsl_code = gsl_code.HostRead();
4248 for (int i = 0; i < gsl_code.Size(); i++)
4249 {
4250 if (h_gsl_code[i] == 2)
4251 {
4252 nf_idxs.Append(i);
4253 }
4254 }
4255 return nf_idxs;
4256}
4257
4259 Array<unsigned int> &recv_elem, Vector &recv_ref,
4260 Array<unsigned int> &recv_code)
4261{
4262 MFEM_VERIFY(points_cnt >= 0,
4263 "Invalid size. Please make sure to call FindPoints method "
4264 "before calling this function.");
4265
4266 // Pack data to send via crystal router
4267 struct gslib::array *outpt = new gslib::array;
4268
4269 struct out_pt { double rst[3]; uint index, elem, proc, code; };
4270 struct out_pt *pt;
4271 array_init(struct out_pt, outpt, points_cnt);
4272 outpt->n=points_cnt;
4273 pt = (struct out_pt *)outpt->ptr;
4274
4275 for (int index = 0; index < points_cnt; index++)
4276 {
4277 pt->index = index;
4278 pt->elem = gsl_mfem_elem[index];
4279 pt->proc = gsl_proc[index];
4280 pt->code = gsl_code[index];
4281 for (int d = 0; d < dim; ++d)
4282 {
4283 pt->rst[d]= gsl_mfem_ref(index*dim + d);
4284 }
4285 ++pt;
4286 }
4287
4288 // Transfer data to target MPI ranks
4289 sarray_transfer(struct out_pt, outpt, proc, 1, cr);
4290
4291 // Store received data
4292 const int points_recv = outpt->n;
4293 recv_proc.SetSize(points_recv);
4294 recv_elem.SetSize(points_recv);
4295 recv_index.SetSize(points_recv);
4296 recv_code.SetSize(points_recv);
4297 recv_ref.SetSize(points_recv*dim);
4298
4299 pt = (struct out_pt *)outpt->ptr;
4300 for (int index = 0; index < points_recv; index++)
4301 {
4302 recv_index[index] = pt->index;
4303 recv_elem[index] = pt->elem;
4304 recv_proc[index] = pt->proc;
4305 recv_code[index] = pt->code;
4306 for (int d = 0; d < dim; ++d)
4307 {
4308 recv_ref(index*dim + d)= pt->rst[d];
4309 }
4310 ++pt;
4311 }
4312
4313 array_free(outpt);
4314 delete outpt;
4315}
4316
4318 const int vdim,
4319 const int ordering,
4320 Vector &field_out) const
4321{
4322 const int points_recv = recv_index.Size();;
4323 MFEM_VERIFY(points_recv == 0 ||
4324 int_vals.Size() % points_recv == 0,
4325 "Incompatible size. Please return interpolated values"
4326 "corresponding to points received using"
4327 "SendCoordinatesToOwningProcessors.");
4328 field_out.SetSize(points_cnt*vdim);
4329
4330 for (int v = 0; v < vdim; v++)
4331 {
4332 // Pack data to send via crystal router
4333 struct gslib::array *outpt = new gslib::array;
4334 struct out_pt { double val; uint index, proc; };
4335 struct out_pt *pt;
4336 array_init(struct out_pt, outpt, points_recv);
4337 outpt->n=points_recv;
4338 pt = (struct out_pt *)outpt->ptr;
4339 for (int index = 0; index < points_recv; index++)
4340 {
4341 pt->index = recv_index[index];
4342 pt->proc = recv_proc[index];
4343 pt->val = ordering == Ordering::byNODES ?
4344 int_vals(index + v*points_recv) :
4345 int_vals(index*vdim + v);
4346 ++pt;
4347 }
4348
4349 // Transfer data to target MPI ranks
4350 sarray_transfer(struct out_pt, outpt, proc, 1, cr);
4351
4352 // Store received data
4353 MFEM_VERIFY(outpt->n == static_cast<size_t>(points_cnt),
4354 "Incompatible size. Number of points "
4355 "received does not match the number of points originally "
4356 "found using FindPoints.");
4357
4358 pt = (struct out_pt *)outpt->ptr;
4359 for (int index = 0; index < points_cnt; index++)
4360 {
4361 int idx = ordering == Ordering::byNODES ?
4362 pt->index + v*points_cnt :
4363 pt->index*vdim + v;
4364 field_out(idx) = pt->val;
4365 ++pt;
4366 }
4367
4368 array_free(outpt);
4369 delete outpt;
4370 }
4371}
4372
4374{
4375 MFEM_VERIFY(setupflag, "Call FindPointsGSLIB::Setup method first");
4376 auto *findptsData3 = (gslib::findpts_data_3 *)this->fdataD;
4377 auto *findptsData2 = (gslib::findpts_data_2 *)this->fdataD;
4378 int nve = spacedim == 2 ? 4 : 8;
4379 int nel = NE_split_total;
4380 aabb.SetSize(spacedim*nve*nel);
4381 auto h_bb_ptr = DEV.bb.HostRead();
4382
4383 if (spacedim == 3)
4384 {
4385 for (int e = 0; e < nel; e++)
4386 {
4387 Vector minn(spacedim), maxx(spacedim);
4388 if (dim == spacedim)
4389 {
4390 auto box = findptsData3->local.obb[e];
4391 for (int d = 0; d < spacedim; d++)
4392 {
4393 minn[d] = box.x[d].min;
4394 maxx[d] = box.x[d].max;
4395 }
4396 }
4397 else
4398 {
4399 const int n_el_ents = obb_check ?
4400 (3*spacedim + spacedim*spacedim) : (2*spacedim);
4401 const int min_off = obb_check ? spacedim : 0;
4402 const int max_off = obb_check ? 2*spacedim : spacedim;
4403 for (int d = 0; d < spacedim; d++)
4404 {
4405 minn[d] = h_bb_ptr[e*n_el_ents + min_off + d];
4406 maxx[d] = h_bb_ptr[e*n_el_ents + max_off + d];
4407 }
4408 }
4409 int c = 0;
4410 aabb(e*nve*spacedim + c++) = minn[0]; /* first vertex - x */
4411 aabb(e*nve*spacedim + c++) = minn[1]; /* y */
4412 aabb(e*nve*spacedim + c++) = minn[2]; /* z */
4413 aabb(e*nve*spacedim + c++) = maxx[0]; /* second vertex - x */
4414 aabb(e*nve*spacedim + c++) = minn[1]; /* . */
4415 aabb(e*nve*spacedim + c++) = minn[2]; /* . */
4416 aabb(e*nve*spacedim + c++) = maxx[0];
4417 aabb(e*nve*spacedim + c++) = maxx[1];
4418 aabb(e*nve*spacedim + c++) = minn[2];
4419 aabb(e*nve*spacedim + c++) = minn[0];
4420 aabb(e*nve*spacedim + c++) = maxx[1];
4421 aabb(e*nve*spacedim + c++) = minn[2];
4422 aabb(e*nve*spacedim + c++) = minn[0];
4423 aabb(e*nve*spacedim + c++) = minn[1];
4424 aabb(e*nve*spacedim + c++) = maxx[2];
4425 aabb(e*nve*spacedim + c++) = maxx[0];
4426 aabb(e*nve*spacedim + c++) = minn[1];
4427 aabb(e*nve*spacedim + c++) = maxx[2];
4428 aabb(e*nve*spacedim + c++) = maxx[0];
4429 aabb(e*nve*spacedim + c++) = maxx[1];
4430 aabb(e*nve*spacedim + c++) = maxx[2];
4431 aabb(e*nve*spacedim + c++) = minn[0];
4432 aabb(e*nve*spacedim + c++) = maxx[1];
4433 aabb(e*nve*spacedim + c++) = maxx[2];
4434 }
4435 }
4436 else // spacedim = 2
4437 {
4438 for (int e = 0; e < nel; e++)
4439 {
4440 Vector minn(spacedim), maxx(spacedim);
4441 if (dim == spacedim)
4442 {
4443 auto box = findptsData2->local.obb[e];
4444 for (int d = 0; d < spacedim; d++)
4445 {
4446 minn[d] = box.x[d].min;
4447 maxx[d] = box.x[d].max;
4448 }
4449 }
4450 else
4451 {
4452 const int n_el_ents = obb_check ? (3*spacedim + spacedim*spacedim) :
4453 (2*spacedim);
4454 const int min_off = obb_check ? spacedim : 0;
4455 const int max_off = obb_check ? 2*spacedim : spacedim;
4456 for (int d = 0; d < spacedim; d++)
4457 {
4458 minn[d] = h_bb_ptr[e*n_el_ents + min_off + d];
4459 maxx[d] = h_bb_ptr[e*n_el_ents + max_off + d];
4460 }
4461 }
4462 aabb(e*nve*spacedim + 0) = minn[0]; /* first vertex - x */
4463 aabb(e*nve*spacedim + 1) = minn[1]; /* y */
4464 aabb(e*nve*spacedim + 2) = maxx[0]; /* second vertex - x */
4465 aabb(e*nve*spacedim + 3) = minn[1]; /* . */
4466 aabb(e*nve*spacedim + 4) = maxx[0]; /* . */
4467 aabb(e*nve*spacedim + 5) = maxx[1];
4468 aabb(e*nve*spacedim + 6) = minn[0];
4469 aabb(e*nve*spacedim + 7) = maxx[1];
4470 }
4471 }
4472}
4473
4475{
4476 MFEM_VERIFY(setupflag, "Call FindPointsGSLIB::Setup method first");
4477 if (type != 0)
4478 {
4479 MFEM_VERIFY(obb_check || dim == spacedim,
4480 "Oriented bounding boxes are not available when obb_check is false");
4481 }
4482 const unsigned int save_rank = 0;
4483 const unsigned int myid = gsl_comm->id;
4484 Vector bbvert;
4485 if (type == 0)
4486 {
4488 }
4489 else
4490 {
4491 DenseTensor obbA;
4492 Vector obbC;
4493 GetOrientedBoundingBoxes(obbA, obbC, bbvert);
4494 }
4495 int nve = spacedim == 2 ? 4 : 8;
4496 int nel = NE_split_total;
4497 int ne_glob = nel;
4498#ifdef MFEM_USE_MPI
4499 MPI_Allreduce(&nel, &ne_glob, 1, MPI_INT, MPI_SUM, gsl_comm->c);
4500#endif
4501
4502
4503 int nverts = nve*ne_glob;
4504 Mesh *meshbb = nullptr;
4505 if (myid == save_rank)
4506 {
4507 meshbb = new Mesh(spacedim, nverts, ne_glob, 0, spacedim);
4508 }
4509
4510 int nsend = nel*nve*spacedim;
4511 MFEM_VERIFY(nsend == bbvert.Size(),
4512 "Inconsistent size of bounding box vertices");
4513 int nrecv = 0;
4514#ifdef MFEM_USE_MPI
4515 MPI_Status status;
4516#endif
4517 int vidx = 0;
4518 int eidx = 0;
4519 if (myid == save_rank)
4520 {
4521 for (int p = 0; (unsigned)p < gsl_comm->np; p++)
4522 {
4523 if (static_cast<unsigned int>(p) != save_rank)
4524 {
4525#ifdef MFEM_USE_MPI
4526 MPI_Recv(&nrecv, 1, MPI_INT, p, 444, gsl_comm->c, &status);
4527 bbvert.SetSize(nrecv);
4528 if (nrecv)
4529 {
4530 MPI_Recv(bbvert.GetData(), nrecv, MPI_DOUBLE, p, 445, gsl_comm->c, &status);
4531 }
4532#endif
4533 }
4534 else
4535 {
4536 nrecv = nsend;
4537 }
4538 int nel_recv = nrecv/(spacedim*nve);
4539 for (int e = 0; e < nel_recv; e++)
4540 {
4541 for (int j = 0; j < nve; j++)
4542 {
4543 Vector ver(bbvert.GetData() + e*nve*spacedim + j*spacedim, spacedim);
4544 meshbb->AddVertex(ver);
4545 }
4546
4547 if (spacedim == 2)
4548 {
4549 const int inds[4] = {vidx++, vidx++, vidx++, vidx++};
4550 int attr = eidx+1;
4551 meshbb->AddQuad(inds, attr);
4552 eidx++;
4553 }
4554 else
4555 {
4556 const int inds[8] = {vidx++, vidx++, vidx++, vidx++,
4557 vidx++, vidx++, vidx++, vidx++
4558 };
4559 meshbb->AddHex(inds, (eidx++)+1);
4560 }
4561 }
4562 }
4563 if (spacedim == 2)
4564 {
4565 meshbb->FinalizeQuadMesh(1, 1, true);
4566 }
4567 else
4568 {
4569 meshbb->FinalizeHexMesh(1, 1, true);
4570 }
4571 }
4572 else
4573 {
4574#ifdef MFEM_USE_MPI
4575 MPI_Send(&nsend, 1, MPI_INT, save_rank, 444, gsl_comm->c);
4576 if (nsend)
4577 {
4578 MPI_Send(bbvert.GetData(), nsend, MPI_DOUBLE, save_rank, 445, gsl_comm->c);
4579 }
4580#endif
4581 }
4582#ifdef MFEM_USE_MPI
4583 MPI_Barrier(gsl_comm->c);
4584#endif
4585
4586 return meshbb;
4587}
4588
4590 Vector &obbV) const
4591{
4592 MFEM_VERIFY(setupflag, "Call FindPointsGSLIB::Setup method first");
4593 MFEM_VERIFY(obb_check || dim == spacedim,
4594 "Oriented bounding boxes are not available when obb_check is false");
4595 auto *findptsData3 = (gslib::findpts_data_3 *)this->fdataD;
4596 auto *findptsData2 = (gslib::findpts_data_2 *)this->fdataD;
4597 int nve = spacedim == 2 ? 4 : 8;
4598 int nel = NE_split_total;
4599
4600 obbA.SetSize(spacedim, spacedim, nel);
4601 obbC.SetSize(spacedim*nel);
4602 obbV.SetSize(spacedim*nve*nel);
4603 if (spacedim == 3)
4604 {
4605 for (int e = 0; e < nel; e++)
4606 {
4607 double *Ad = obbA.GetData(e);
4608 if (dim == spacedim)
4609 {
4610 auto box = findptsData3->local.obb[e];
4611 for (int d = 0; d < spacedim; d++)
4612 {
4613 obbC(e*spacedim + d) = box.c0[d];
4614 }
4615 for (int i = 0; i < spacedim; i++)
4616 {
4617 for (int j = 0; j < spacedim; j++)
4618 {
4619 Ad[i*spacedim + j] = box.A[i + j*spacedim]; // GSLIB uses row-major storage
4620 }
4621 }
4622 }
4623 else
4624 {
4625 int n_el_ents = 18;
4626 for (int d = 0; d < spacedim; d++)
4627 {
4628 obbC(e*spacedim + d) = DEV.bb(n_el_ents*e + d);
4629 }
4630 for (int i = 0; i < spacedim; i++)
4631 {
4632 for (int j = 0; j < spacedim; j++)
4633 {
4634 Ad[i*spacedim + j] = DEV.bb[n_el_ents*e + 9 + j*spacedim+i];
4635 }
4636 }
4637 }
4638
4639 DenseMatrix Amat = obbA(e);
4640 Amat.Invert();
4641 Vector center(obbC.GetData() + e*spacedim, spacedim);
4642
4643 Vector v1(spacedim);
4644 Vector temp;
4645 v1(0) = -1.0; v1(1) = -1.0; v1(2) = -1.0;
4646 temp.SetDataAndSize(obbV.GetData() + e*nve*spacedim + 0, spacedim);
4647 Amat.Mult(v1, temp);
4648 temp += center;
4649 v1(0) = 1.0; v1(1) = -1.0; v1(2) = -1.0;
4650 temp.SetDataAndSize(obbV.GetData() + e*nve*spacedim + 3, spacedim);
4651 Amat.Mult(v1, temp);
4652 temp += center;
4653 v1(0) = 1.0; v1(1) = 1.0; v1(2) = -1.0;
4654 temp.SetDataAndSize(obbV.GetData() + e*nve*spacedim + 6, spacedim);
4655 Amat.Mult(v1, temp);
4656 temp += center;
4657 v1(0) = -1.0; v1(1) = 1.0; v1(2) = -1.0;
4658 temp.SetDataAndSize(obbV.GetData() + e*nve*spacedim + 9, spacedim);
4659 Amat.Mult(v1, temp);
4660 temp += center;
4661 v1(0) = -1.0; v1(1) = -1.0; v1(2) = 1.0;
4662 temp.SetDataAndSize(obbV.GetData() + e*nve*spacedim + 12, spacedim);
4663 Amat.Mult(v1, temp);
4664 temp += center;
4665 v1(0) = 1.0; v1(1) = -1.0; v1(2) = 1.0;
4666 temp.SetDataAndSize(obbV.GetData() + e*nve*spacedim + 15, spacedim);
4667 Amat.Mult(v1, temp);
4668 temp += center;
4669 v1(0) = 1.0; v1(1) = 1.0; v1(2) = 1.0;
4670 temp.SetDataAndSize(obbV.GetData() + e*nve*spacedim + 18, spacedim);
4671 Amat.Mult(v1, temp);
4672 temp += center;
4673 v1(0) = -1.0; v1(1) = 1.0; v1(2) = 1.0;
4674 temp.SetDataAndSize(obbV.GetData() + e*nve*spacedim + 21, spacedim);
4675 Amat.Mult(v1, temp);
4676 temp += center;
4677 }
4678 }
4679 else // spacedim = 2
4680 {
4681 for (int e = 0; e < nel; e++)
4682 {
4683 double *Ad = obbA.GetData(e);
4684 if (dim == spacedim)
4685 {
4686 auto box = findptsData2->local.obb[e];
4687 for (int d = 0; d < spacedim; d++)
4688 {
4689 obbC(e*spacedim + d) = box.c0[d];
4690 }
4691 for (int i = 0; i < spacedim; i++)
4692 {
4693 for (int j = 0; j < spacedim; j++)
4694 {
4695 Ad[i*spacedim + j] = box.A[i + j*spacedim]; // GSLIB uses row-major storage
4696 }
4697 }
4698 }
4699 else
4700 {
4701 int n_el_ents = 10;
4702 for (int d = 0; d < spacedim; d++)
4703 {
4704 obbC(e*spacedim + d) = DEV.bb(n_el_ents*e + d);
4705 }
4706 for (int i = 0; i < spacedim; i++)
4707 {
4708 for (int j = 0; j < spacedim; j++)
4709 {
4710 Ad[i*spacedim + j] = DEV.bb[n_el_ents*e + 6 + j*spacedim+i];
4711 }
4712 }
4713 }
4714
4715
4716 DenseMatrix Amat = obbA(e);
4717 Amat.Invert();
4718 Vector center(obbC.GetData() + e*spacedim, spacedim);
4719
4720 Vector v1(spacedim);
4721 Vector temp;
4722 v1(0) = -1.0; v1(1) = -1.0;
4723 temp.SetDataAndSize(obbV.GetData() + e*nve*spacedim + 0, spacedim);
4724 Amat.Mult(v1, temp);
4725 temp += center;
4726 v1(0) = 1.0; v1(1) = -1.0;
4727 temp.SetDataAndSize(obbV.GetData() + e*nve*spacedim + 2, spacedim);
4728 Amat.Mult(v1, temp);
4729 temp += center;
4730 v1(0) = 1.0; v1(1) = 1.0;
4731 temp.SetDataAndSize(obbV.GetData() + e*nve*spacedim + 4, spacedim);
4732 Amat.Mult(v1, temp);
4733 temp += center;
4734 v1(0) = -1.0; v1(1) = 1.0;
4735 temp.SetDataAndSize(obbV.GetData() + e*nve*spacedim + 6, spacedim);
4736 Amat.Mult(v1, temp);
4737 temp += center;
4738 }
4739 }
4740}
4741
4742void OversetFindPointsGSLIB::Setup(Mesh &m, const int meshid,
4743 GridFunction *gfmax,
4744 const double bbox_rel_size_inc,
4745 const double newt_tol,
4746 const int npt_max)
4747{
4748 MFEM_VERIFY(m.GetNodes() != nullptr, "Mesh nodes are required.");
4749 const int meshOrder = m.GetNodes()->FESpace()->GetMaxElementOrder();
4750 const int gfOrder = gfmax ? gfmax->FESpace()->GetMaxElementOrder() :
4751 meshOrder;
4752 MFEM_VERIFY(meshOrder == gfOrder,
4753 "Mesh order must match gfmax order in OversetFindPointsGSLIB.");
4754
4755 // FreeData if OversetFindPointsGSLIB::Setup has been called already
4756 if (setupflag) { FreeData(); }
4757 SetupCrystal();
4758
4759 mesh = &m;
4760 dim = mesh->Dimension();
4761 spacedim = dim;
4763 unsigned dof1D = fe->GetOrder() + 1;
4764
4766
4768
4769 MFEM_ASSERT(meshid>=0, " The ID should be greater than or equal to 0.");
4770
4771 const int pts_cnt = gsl_mesh.Size()/dim,
4772 NEtot = pts_cnt/(int)pow(dof1D, dim);
4773
4774 distfint.SetSize(pts_cnt);
4775 if (!gfmax)
4776 {
4777 distfint = 0.0;
4778 }
4779 else
4780 {
4781 GetNodalValues(gfmax, distfint);
4782 }
4783 u_meshid = (unsigned int)meshid;
4784
4785 if (dim == 2)
4786 {
4787 unsigned nr[2] = { dof1D, dof1D };
4788 unsigned mr[2] = { 2*dof1D, 2*dof1D };
4789 double * const elx[2] =
4790 {
4791 pts_cnt == 0 ? nullptr : &gsl_mesh(0),
4792 pts_cnt == 0 ? nullptr : &gsl_mesh(pts_cnt)
4793 };
4794 fdataD = findptsms_setup_2(gsl_comm, elx, nr, NEtot, mr,
4795 bbox_rel_size_inc, pts_cnt, pts_cnt,
4796 npt_max, newt_tol,
4797 &u_meshid, &distfint(0));
4798 }
4799 else
4800 {
4801 unsigned nr[3] = { dof1D, dof1D, dof1D };
4802 unsigned mr[3] = { 2*dof1D, 2*dof1D, 2*dof1D };
4803 double * const elx[3] =
4804 {
4805 pts_cnt == 0 ? nullptr : &gsl_mesh(0),
4806 pts_cnt == 0 ? nullptr : &gsl_mesh(pts_cnt),
4807 pts_cnt == 0 ? nullptr : &gsl_mesh(2*pts_cnt)
4808 };
4809 fdataD = findptsms_setup_3(gsl_comm, elx, nr, NEtot, mr,
4810 bbox_rel_size_inc, pts_cnt, pts_cnt,
4811 npt_max, newt_tol,
4812 &u_meshid, &distfint(0));
4813 }
4814 setupflag = true;
4815 overset = true;
4816}
4817
4819 const Array<unsigned int> &point_id,
4820 const int point_pos_ordering)
4821{
4822 MFEM_VERIFY(setupflag, "Use OversetFindPointsGSLIB::Setup before "
4823 "finding points.");
4824 MFEM_VERIFY(overset, "Please use OversetFindPoints for overlapping grids.");
4825 points_cnt = point_pos.Size() / dim;
4826 unsigned int match = 0; // Don't find points in the mesh if point_id=mesh_id
4827
4833
4834 auto xvFill = [&](const double *xv_base[], unsigned xv_stride[])
4835 {
4836 for (int d = 0; d < dim; d++)
4837 {
4838 if (point_pos_ordering == Ordering::byNODES)
4839 {
4840 xv_base[d] = point_pos.GetData() + d*points_cnt;
4841 xv_stride[d] = sizeof(double);
4842 }
4843 else
4844 {
4845 xv_base[d] = point_pos.GetData() + d;
4846 xv_stride[d] = dim*sizeof(double);
4847 }
4848 }
4849 };
4850 if (dim == 2)
4851 {
4852 auto *findptsData = (gslib::findpts_data_2 *)this->fdataD;
4853 const double *xv_base[2];
4854 unsigned xv_stride[2];
4855 xvFill(xv_base, xv_stride);
4856 findptsms_2(gsl_code.GetData(), sizeof(unsigned int),
4857 gsl_proc.GetData(), sizeof(unsigned int),
4858 gsl_elem.GetData(), sizeof(unsigned int),
4859 gsl_ref.GetData(), sizeof(double) * dim,
4860 gsl_dist.GetData(), sizeof(double),
4861 xv_base, xv_stride,
4862 point_id.GetData(), sizeof(unsigned int), &match,
4863 points_cnt, findptsData);
4864 }
4865 else // dim == 3
4866 {
4867 auto *findptsData = (gslib::findpts_data_3 *)this->fdataD;
4868 const double *xv_base[3];
4869 unsigned xv_stride[3];
4870 xvFill(xv_base, xv_stride);
4871 findptsms_3(gsl_code.GetData(), sizeof(unsigned int),
4872 gsl_proc.GetData(), sizeof(unsigned int),
4873 gsl_elem.GetData(), sizeof(unsigned int),
4874 gsl_ref.GetData(), sizeof(double) * dim,
4875 gsl_dist.GetData(), sizeof(double),
4876 xv_base, xv_stride,
4877 point_id.GetData(), sizeof(unsigned int), &match,
4878 points_cnt, findptsData);
4879 }
4880
4881 // Set the element number and reference position to 0 for points not found
4882 for (int i = 0; i < points_cnt; i++)
4883 {
4884 if (gsl_code[i] == 2 ||
4885 (gsl_code[i] == 1 && gsl_dist(i) > bdr_tol))
4886 {
4887 gsl_elem[i] = 0;
4888 for (int d = 0; d < dim; d++) { gsl_ref(i*dim + d) = -1.; }
4889 gsl_code[i] = 2;
4890 }
4891 }
4892
4893 // Map element number for simplices, and ref_pos from [-1,1] to [0,1] for both
4894 // simplices and quads.
4896}
4897
4899 const Array<unsigned int> &point_id,
4900 const GridFunction &field_in,
4901 Vector &field_out,
4902 const int point_pos_ordering)
4903{
4904 FindPoints(point_pos, point_id, point_pos_ordering);
4905 Interpolate(field_in, field_out);
4906}
4907
4909{
4910 gsl_comm = new gslib::comm;
4911 cr = new gslib::crystal;
4912#ifdef MFEM_USE_MPI
4913 if (!Mpi::IsInitialized()) { Mpi::Init(); }
4914 MPI_Comm comm = MPI_COMM_WORLD;
4915 comm_init(gsl_comm, comm);
4916#else
4917 comm_init(gsl_comm, 0);
4918#endif
4919 crystal_init(cr, gsl_comm);
4920 UpdateIdentifiers(ids);
4921}
4922
4923#ifdef MFEM_USE_MPI
4925 : cr(NULL), gsl_comm(NULL)
4926{
4927 gsl_comm = new gslib::comm;
4928 cr = new gslib::crystal;
4929 comm_init(gsl_comm, comm_);
4930 crystal_init(cr, gsl_comm);
4931 UpdateIdentifiers(ids);
4932}
4933#endif
4934
4936{
4937#ifdef MFEM_USE_MPI
4938 if (!Mpi::IsFinalized()) // currently segfaults inside gslib otherwise
4939#endif
4940 {
4941 crystal_free(cr);
4942 comm_free(gsl_comm);
4943 delete gsl_comm;
4944 delete cr;
4945 gslib_gs_free(gsl_data);
4946 }
4947}
4948
4950{
4951 long long minval = ids.Min();
4952#ifdef MFEM_USE_MPI
4953 MPI_Allreduce(MPI_IN_PLACE, &minval, 1, MPI_LONG_LONG_INT,
4954 MPI_MIN, gsl_comm->c);
4955#endif
4956 MFEM_VERIFY(minval >= 0, "Unique identifier cannot be negative.");
4957 if (gsl_data != nullptr) { gslib_gs_free(gsl_data); }
4958 num_ids = ids.Size();
4959 gsl_data = gslib_gs_setup(ids.GetData(),
4960 ids.Size(),
4961 gsl_comm, 0,
4962 gslib::gs_crystal_router, 0);
4963}
4964
4965void GSOPGSLIB::GS(Vector &senddata, GSOp op)
4966{
4967 MFEM_VERIFY(senddata.Size() == num_ids,
4968 "Incompatible setup and GOP operation.");
4969 if (op == GSOp::ADD)
4970 {
4971 gslib_gs(senddata.GetData(),gslib::gs_double,gslib::gs_add,0,gsl_data,0);
4972 }
4973 else if (op == GSOp::MUL)
4974 {
4975 gslib_gs(senddata.GetData(),gslib::gs_double,gslib::gs_mul,0,gsl_data,0);
4976 }
4977 else if (op == GSOp::MAX)
4978 {
4979 gslib_gs(senddata.GetData(),gslib::gs_double,gslib::gs_max,0,gsl_data,0);
4980 }
4981 else if (op == GSOp::MIN)
4982 {
4983 gslib_gs(senddata.GetData(),gslib::gs_double,gslib::gs_min,0,gsl_data,0);
4984 }
4985 else
4986 {
4987 MFEM_ABORT("Invalid GSOp operation.");
4988 }
4989}
4990
4991#if defined(MFEM_USE_MPI)
4992void GlobalBBoxTensorGridMap::SetupCrystal(const MPI_Comm &comm_)
4993{
4994#if GSLIB_RELEASE_VERSION < 10009
4995 MFEM_ABORT("GSLIB version 1.0.9 or higher is required.");
4996#endif
4997 gsl_comm = new gslib::comm;
4998 cr = new gslib::crystal;
4999 comm_init(gsl_comm, comm_);
5000 gslib::crystal_init(cr, gsl_comm);
5001}
5002
5004{
5005 GridFunction *nodes = pmesh.GetNodes();
5006 const int nel = pmesh.GetNE();
5007 sdim = pmesh.SpaceDimension();
5008 Vector elmin(nel*sdim), elmax(nel*sdim);
5009 elmin = std::numeric_limits<real_t>::max();
5010 elmax = -std::numeric_limits<real_t>::max();
5011 if (!nodes)
5012 {
5013 Array<int> verts;
5014 real_t *coord;
5015 // create bounding boxes from vertex coordinates
5016 for (int e = 0; e < nel; e++)
5017 {
5018 pmesh.GetElementVertices(e, verts);
5019 Vector center(sdim);
5020 for (int v = 0; v < verts.Size(); v++)
5021 {
5022 coord = pmesh.GetVertex(verts[v]);
5023 for (int d = 0; d < sdim; d++)
5024 {
5025 elmin(d*nel + e) = std::min(elmin(d*nel + e), coord[d]);
5026 elmax(d*nel + e) = std::max(elmax(d*nel + e), coord[d]);
5027 }
5028 }
5029 }
5030 }
5031 else
5032 {
5033 int nref = 3;
5034 nodes->GetElementBounds(elmin, elmax, nref);
5035 }
5036 Array<int> nx_arr(sdim);
5037 nx_arr = nx;
5038 Setup(pmesh.GetComm(), elmin, elmax, nel, nx_arr);
5039}
5040
5042 Vector &elmin, Vector &elmax,
5043 int nel, int sdim_,
5044 int n,
5045 bool by_max_size)
5046{
5047 sdim = sdim_;
5048 MFEM_VERIFY(0 < sdim && sdim <= 3,
5049 "GlobalBBoxTensorGridMap only supports spatial dimensions 1, 2, and 3.");
5050 if (nel > 0)
5051 {
5052 MFEM_VERIFY(elmin.Size() == sdim * nel && elmax.Size() == sdim * nel,
5053 "Element bounds size must match dim * nel.");
5054 }
5055 Array<int> nx_arr(sdim);
5056 if (!by_max_size)
5057 {
5058 nx_arr = n;
5059 }
5060 else
5061 {
5062 long long int nx = n;
5063 MPI_Allreduce(MPI_IN_PLACE, &nx, 1, MPI_LONG_LONG, MPI_SUM, comm);
5064 nx = ceil(pow((double)nx,1./sdim));
5065 nx_arr = nx;
5066 }
5067 Setup(comm, elmin, elmax, nel, nx_arr);
5068}
5069
5071 Vector &elmin, Vector &elmax,
5072 int nel, int sdim_,
5073 Array<int> &nx)
5074{
5075 sdim = sdim_;
5076 Setup(comm, elmin, elmax, nel, nx);
5077}
5078
5079void GlobalBBoxTensorGridMap::Setup(const MPI_Comm &comm,
5080 Vector &elmin, Vector &elmax,
5081 int nel, Array<int> &nx)
5082{
5083 SetupCrystal(comm);
5084 MFEM_VERIFY(0 < sdim && sdim <= 3,
5085 "GlobalBBoxTensorGridMap only supports spatial dimensions 1, 2, and 3.");
5086 MFEM_VERIFY(nx.Size() == sdim,
5087 "GlobalBBoxTensorGridMap requires nx to have the same size as the number of dimensions.");
5088 if (nel > 0)
5089 {
5090 MFEM_VERIFY(elmin.Size() == sdim * nel && elmax.Size() == sdim * nel,
5091 "Element bounds size must match dim * nel.");
5092 }
5093 gmap_bnd_min.SetSize(sdim);
5094 gmap_bnd_max.SetSize(sdim);
5095 gmap_fac.SetSize(sdim);
5096 gmap_n.SetSize(sdim);
5097 gmap_n = nx;
5098
5099 long long int global_nel = nel;
5100 MPI_Allreduce(MPI_IN_PLACE, &global_nel, 1, MPI_LONG_LONG, MPI_SUM, comm);
5101
5102 MPI_Comm_size(comm, &num_procs);
5103 int gmap_nd = gmap_n[0];
5104 for (int d = 1; d < sdim; d++)
5105 {
5106 gmap_nd *= gmap_n[d];
5107 }
5108
5109 if (global_nel == 0)
5110 {
5111 gmap_n = 1;
5112 gmap_nd = 1;
5113
5114 // Mark the global bounding box as empty so all point queries return no
5115 // candidate ranks without relying on sentinel infinities.
5116 gmap_bnd_min = 1.0;
5117 gmap_bnd_max = 0.0;
5118 gmap_fac = 0.0;
5119 n_local_cells = (gmap_nd - 1) / num_procs + 1;
5120 ggrid_map.SetSize(n_local_cells + 1);
5121 ggrid_map = n_local_cells + 1;
5122 return;
5123 }
5124
5125 for (int d = 0; d < nx.Size(); d++)
5126 {
5127 MFEM_VERIFY(nx[d] > 0,
5128 "GlobalBBoxTensorGridMap requires positive number of divisions in each dimension.");
5129 }
5130 gmap_bnd_min = std::numeric_limits<real_t>::max();
5131 gmap_bnd_max = -std::numeric_limits<real_t>::max();
5132 if (nel > 0)
5133 {
5134 for (int d = 0; d < sdim; d++)
5135 {
5136 Vector elmind(elmin.GetData() + d*nel, nel);
5137 Vector elmaxd(elmax.GetData() + d*nel, nel);
5138 gmap_bnd_min[d] = elmind.Min();
5139 gmap_bnd_max[d] = elmaxd.Max();
5140 }
5141 }
5142
5143 Vector gmap_bnd_min_loc = gmap_bnd_min;
5144 Vector gmap_bnd_max_loc = gmap_bnd_max;
5145
5146 MPI_Allreduce(MPI_IN_PLACE, gmap_bnd_min.GetData(), sdim,
5147 MFEM_MPI_REAL_T, MPI_MIN, comm);
5148 MPI_Allreduce(MPI_IN_PLACE, gmap_bnd_max.GetData(), sdim,
5149 MFEM_MPI_REAL_T, MPI_MAX, comm);
5150
5151 BBoxTensorGridMap::SetGridFac(gmap_fac, gmap_n, gmap_bnd_min, gmap_bnd_max);
5152
5153 // Grid cell ranges for each element in each dimension
5154 Array<int> elmin_h, elmax_h;
5155 int store_size = BBoxTensorGridMap::GetGridCountAndRange(gmap_n, gmap_fac,
5156 gmap_bnd_min,
5157 gmap_bnd_max,
5158 elmin, elmax,
5159 elmin_h, elmax_h);
5160
5161 Array<int> loc_idx_min(sdim), loc_idx_max(sdim), lh_n(sdim);
5162 int loc_idx_tot = 1;
5163
5164 if (nel > 0)
5165 {
5166 for (int d = 0; d < sdim; d++)
5167 {
5168 BBoxTensorGridMap::GetGridRange(d, gmap_n, gmap_fac,
5169 gmap_bnd_min,
5170 gmap_bnd_min_loc[d],
5171 gmap_bnd_max_loc[d],
5172 loc_idx_min[d], loc_idx_max[d]);
5173 lh_n[d] = loc_idx_max[d] - loc_idx_min[d];
5174 loc_idx_tot *= lh_n[d];
5175 }
5176 }
5177 else
5178 {
5179 loc_idx_min = 0;
5180 loc_idx_max = 1;
5181 lh_n = 1;
5182 }
5183
5184 struct hashInfo_s
5185 {
5186 unsigned int index, proc;
5187 };
5188 struct gslib::array hashInfo_pt;
5189 array_init(struct hashInfo_s, &hashInfo_pt, store_size);
5190 hashInfo_pt.n=store_size;
5191
5192 struct hashInfo_s *pt = (struct hashInfo_s *)hashInfo_pt.ptr;
5193 Array<int> marker(loc_idx_tot);
5194 marker = 0;
5195
5196 for (int e = 0; e < nel; e++)
5197 {
5198 int klim = sdim < 3 ? 1 : (elmax_h[2*nel+e]-elmin_h[2*nel+e]);
5199 int jlim = sdim < 2 ? 1 : (elmax_h[1*nel+e]-elmin_h[1*nel+e]);
5200 int ilim = (elmax_h[0*nel+e]-elmin_h[0*nel+e]);
5201 for (int k = 0; k < klim; k++)
5202 {
5203 int koff = sdim < 3 ? 0 : (elmin_h[2*nel+e] + k) * gmap_n[0] * gmap_n[1];
5204 int koff_loc = sdim < 3 ? 0 :
5205 (elmin_h[2*nel+e]+k - loc_idx_min[2])*lh_n[0] * lh_n[1];
5206 for (int j = 0; j < jlim; j++)
5207 {
5208 int joff = sdim < 2 ? 0 : (elmin_h[1*nel + e] + j) * gmap_n[0];
5209 int joff_loc = sdim < 2 ? 0 :
5210 (elmin_h[1*nel+e]+j - loc_idx_min[1])*lh_n[0];
5211 for (int i = 0; i < ilim; i++)
5212 {
5213 int ioff = elmin_h[0*nel + e] + i;
5214 int ioff_loc = elmin_h[0*nel+e]+i - loc_idx_min[0];
5215 int idx = ioff + joff + koff;
5216 int idx_loc = ioff_loc + joff_loc + koff_loc;
5217 if (marker[idx_loc] == 1) { continue; }
5218 pt->proc = idx % num_procs;
5219 pt->index = idx / num_procs;
5220 marker[idx_loc] = 1;
5221 ++pt;
5222 }
5223 }
5224 }
5225 }
5226 MPI_Barrier(comm);
5227 int npts = marker.Sum();
5228 hashInfo_pt.n = npts;
5229
5230 // transfer info to other ranks and sort by index
5231#if GSLIB_RELEASE_VERSION >= 10009
5232 sarray_transfer(struct hashInfo_s, &hashInfo_pt, proc, 1, cr);
5233 sarray_sort(struct hashInfo_s, hashInfo_pt.ptr, hashInfo_pt.n,
5234 index, 0, &(cr->data));
5235#endif
5236
5237 int nrecv = hashInfo_pt.n;
5238
5239 n_local_cells = (gmap_nd-1)/num_procs+1;
5240 ggrid_map.SetSize(n_local_cells + 1 + nrecv);
5241 ggrid_map = 0;
5242 Array<int> hash_el_count(n_local_cells);
5243 hash_el_count = 0;
5244
5245 pt = (struct hashInfo_s *)hashInfo_pt.ptr;
5246 for (int i = 0; i < nrecv; i++)
5247 {
5248 int idx = pt->index;
5249 hash_el_count[idx]++;
5250 ++pt;
5251 }
5252
5253 ggrid_map[0] = n_local_cells + 1;
5254 for (int e = 0; e < n_local_cells; e++)
5255 {
5256 ggrid_map[e + 1] = ggrid_map[e] + hash_el_count[e];
5257 }
5258
5259 pt = (struct hashInfo_s *)hashInfo_pt.ptr;
5260 for (int i = 0; i < nrecv; i++)
5261 {
5262 int idx = pt->index;
5263 int proc = pt->proc;
5264 ggrid_map[ggrid_map[idx+1]-hash_el_count[idx]]=proc;
5265 hash_el_count[idx]--;
5266 ++pt;
5267 }
5268
5269 array_free(&hashInfo_pt);
5270 MPI_Barrier(comm);
5271}
5272
5273int GlobalBBoxTensorGridMap::GetGlobalGridCellFromPoint(Vector &xyz) const
5274{
5275 MFEM_ASSERT(xyz.Size() == sdim,
5276 "Point must have the same dimension as the hash.");
5277 int sum = 0;
5278 for (int d = sdim-1; d >= 0; --d)
5279 {
5280 if (xyz(d) < gmap_bnd_min(d) || xyz(d) > gmap_bnd_max(d))
5281 {
5282 return -1; // Point is outside the bounds of the hash
5283 }
5284 sum *= gmap_n[d];
5285 int i = (int)floor((xyz(d) - gmap_bnd_min(d)) * gmap_fac[d]);
5286 sum += i < 0 ? 0 : (gmap_n[d] - 1 < i ? gmap_n[d] - 1 : i);
5287 }
5288 return sum;
5289}
5290
5291void GlobalBBoxTensorGridMap::GlobalGridCellToProcAndLocalIndex(int i,
5292 int &proc,
5293 int &idx) const
5294{
5295 proc = i % num_procs;
5296 idx = i / num_procs;
5297}
5298
5299void GlobalBBoxTensorGridMap::GetProcAndLocalIndexFromPoint(Vector &xyz,
5300 int &proc, int &idx) const
5301{
5302 int cell = GetGlobalGridCellFromPoint(xyz);
5303 if (cell < 0)
5304 {
5305 proc = -1;
5306 idx = -1;
5307 return; // Point is outside the bounds of the grid
5308 }
5309 GlobalGridCellToProcAndLocalIndex(cell, proc, idx);
5310}
5311
5313 std::map<int,
5314 std::vector<int>> &pt_to_procs) const
5315{
5316 MFEM_ASSERT(xyz.Size() % sdim == 0,
5317 "Point array size must be a multiple of the grid dimension.");
5318 int npts = xyz.Size() / sdim;
5319 pt_to_procs.clear();
5320 for (int i = 0; i < npts; i++)
5321 {
5322 pt_to_procs[i]; // ensure key exists for every point
5323 }
5324
5325 // struct to hold point info:
5326 // info: holds local grid cell index when first sent.
5327 // holds proc when returned back.
5328 // proc: holds the proc that the grid cell info is on.
5329 // loc_index: local index of the point in the input vector.
5330 struct ptInfo_s
5331 {
5332 unsigned int info, proc, loc_index;
5333 };
5334 struct gslib::array ptInfo_pt;
5335 array_init(struct ptInfo_s, &ptInfo_pt, npts);
5336 struct ptInfo_s *pt = (struct ptInfo_s *)ptInfo_pt.ptr;
5337 struct ptInfo_s *pt_begin = pt;
5338
5339 for (int i = 0; i < npts; i++)
5340 {
5341 Vector pt_xyz(sdim);
5342 for (int d = 0; d < sdim; d++)
5343 {
5344 pt_xyz(d) = ordering == 0 ? xyz(d*npts + i) : xyz(i*sdim + d);
5345 }
5346
5347 int cell = GetGlobalGridCellFromPoint(pt_xyz);
5348 if (cell < 0)
5349 {
5350 continue; // Point is outside the bounds of the hash
5351 }
5352 int proc, idx;
5353 GlobalGridCellToProcAndLocalIndex(cell, proc, idx);
5354 pt->info = idx; // local grid cell index on proc
5355 pt->proc = proc; // proc that the grid cell info is on
5356 pt->loc_index = i; // local index of the point in the input vector
5357 ++pt;
5358 }
5359 ptInfo_pt.n = pt - pt_begin;
5360 MPI_Barrier(gsl_comm->c);
5361
5362 // transfer info to ranks that hold each hash cell's info
5363 sarray_transfer(struct ptInfo_s, &ptInfo_pt, proc, 1, cr);
5364
5365 int nrecv = ptInfo_pt.n;
5366 pt = (struct ptInfo_s *)ptInfo_pt.ptr;
5367 int ncount = 0;
5368 for (int i = 0; i < nrecv; i++)
5369 {
5370 int idx = pt->info;
5371 int loc_count = ggrid_map[idx+1]-ggrid_map[idx];
5372 ncount += loc_count;
5373 ++pt;
5374 }
5375
5376 struct gslib::array sendptInfo_pt;
5377 array_init(struct ptInfo_s, &sendptInfo_pt, ncount);
5378 sendptInfo_pt.n=ncount;
5379 struct ptInfo_s *spt = (struct ptInfo_s *)sendptInfo_pt.ptr;
5380 pt = (struct ptInfo_s *)ptInfo_pt.ptr;
5381
5382 for (int i = 0; i < nrecv; i++)
5383 {
5384 Array<int> procs = MapCellToProcs(pt->info); // map for local index
5385 for (int k = 0; k < procs.Size(); k++)
5386 {
5387 spt->info = procs[k]; // proc that has contribution to this cell
5388 spt->proc = pt->proc; // proc that sent this point
5389 spt->loc_index = pt->loc_index; // local index of the point originally
5390 ++spt;
5391 }
5392 ++pt;
5393 }
5394
5395 array_free(&ptInfo_pt);
5396 // transfer information back to ranks where the query originated from.
5397 sarray_transfer(struct ptInfo_s, &sendptInfo_pt, proc, 1, cr);
5398
5399 nrecv = sendptInfo_pt.n;
5400 spt = (struct ptInfo_s *)sendptInfo_pt.ptr;
5401
5402 for (int i =0; i < nrecv; i++)
5403 {
5404 int pt_idx = spt->loc_index; // local index of the point
5405 int proc = spt->info; // proc that could be holding this point
5406 pt_to_procs[pt_idx].push_back(proc);
5407 ++spt;
5408 }
5409
5410 array_free(&sendptInfo_pt);
5411 MPI_Barrier(gsl_comm->c);
5412}
5413
5414Array<int> GlobalBBoxTensorGridMap::MapCellToProcs(int l_idx) const
5415{
5416 MFEM_ASSERT(l_idx >= 0 && l_idx < n_local_cells,
5417 "Access element " << l_idx << " of local hash with cells = "
5418 << n_local_cells);
5419 int start = ggrid_map[l_idx];
5420 int end = ggrid_map[l_idx + 1];
5421 Array<int> elements(end - start);
5422 for (int j = start; j < end; j++)
5423 {
5424 elements[j - start] = ggrid_map[j];
5425 }
5426 return elements;
5427}
5428
5430{
5431 if (!Mpi::IsFinalized()) // currently segfaults inside gslib otherwise
5432 {
5433 crystal_free(cr);
5434 comm_free(gsl_comm);
5435 delete gsl_comm;
5436 delete cr;
5437 }
5438}
5439
5440#endif // defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
5441
5442} // namespace mfem
5443#undef CODE_INTERNAL
5444#undef CODE_BORDER
5445#undef CODE_NOT_FOUND
5446
5447#endif // MFEM_USE_GSLIB
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
Definition array.hpp:414
T * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:426
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:869
T Min() const
Find the minimal element in the array, using the comparison operator < for class T.
Definition array.cpp:86
int Size() const
Return the logical size of the array.
Definition array.hpp:192
void DeleteAll()
Delete the whole array.
Definition array.hpp:1062
T * Write(bool on_dev=true)
Shortcut for mfem::Write(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:418
int Append(const T &el)
Append element 'el' to array, resize if necessary.
Definition array.hpp:941
T * GetData()
Returns the data.
Definition array.hpp:159
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
Definition array.hpp:410
T * HostReadWrite()
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), false).
Definition array.hpp:430
T * HostWrite()
Shortcut for mfem::Write(a.GetMemory(), a.Size(), false).
Definition array.hpp:422
Map a point in physical space to candidate elements of a curved mesh.
const Array< int > & GetGridN() const
Return the grid resolution (number of cells) in each direction.
static void GetGridRange(const int d, const Array< int > &lh_n, const Vector &lh_fac, const Vector &lh_bnd_min, const real_t &xmin, const real_t &xmax, int &imin, int &imax)
Get local (1D) indices for cells of tensor grid that intersect with the given bounding box.
const Vector & GetGridFac() const
Return the number of grid cells per unit extent in each direction.
static void SetGridFac(Vector &lh_fac, const Array< int > &nx, const Vector &lh_bnd_min, const Vector &lh_bnd_max)
Set grid fac - number of grid cells per unit grid extent.
const Vector & GetGridMin() const
Return the minimum extent of the grid in each direction.
static int GetGridCountAndRange(const Array< int > &lh_n, const Vector &lh_fac, const Vector &lh_bnd_min, const Vector &lh_bnd_max, const Vector &elmin, const Vector &elmax, Array< int > &elmin_h, Array< int > &elmax_h)
Get grid count and range - total number of grid cells that intersect with all elements of the mesh an...
const Array< unsigned int > & GetGridMap() const
@ GaussLobatto
Closed type.
Definition fe_base.hpp:36
Data type dense matrix using column-major storage.
Definition densemat.hpp:24
void Mult(const real_t *x, real_t *y) const
Matrix vector multiplication.
Definition densemat.cpp:108
real_t * Data() const
Returns the matrix data array. Warning: this method casts away constness.
Definition densemat.hpp:131
void Invert()
Replaces the current matrix with its inverse.
Definition densemat.cpp:674
Rank 3 tensor (array of matrices)
void SetSize(int i, int j, int k, MemoryType mt_=MemoryType::PRESERVE)
real_t * GetData(int k)
static MemoryType GetMemoryType()
(DEPRECATED) Equivalent to GetDeviceMemoryType().
Definition device.hpp:302
static bool IsEnabled()
Return true if any backend other than Backend::CPU is enabled.
Definition device.hpp:252
Geometry::Type GetGeometryType() const
Definition element.hpp:55
Type
Constants for the classes derived from Element.
Definition element.hpp:41
FindPointsGSLIB can robustly evaluate a GridFunction on an arbitrary collection of points....
Definition gslib.hpp:115
virtual void DistributePointInfoToOwningMPIRanks(Array< unsigned int > &recv_elem, Vector &recv_ref, Array< unsigned int > &recv_code)
Definition gslib.cpp:4258
void GetAxisAlignedBoundingBoxes(Vector &aabb) const
Definition gslib.cpp:4373
virtual ~FindPointsGSLIB()
Definition gslib.cpp:240
void FindPointsEdgeLocal2(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 edge elements.
void FindPointsOnDevice(const Vector &point_pos, const int point_pos_ordering=Ordering::byNODES)
Searches positions given in physical space by point_pos. These positions can be ordered byNodes: (XXX...
Definition gslib.cpp:1634
Array< unsigned int > gsl_code
Definition gslib.hpp:137
Array< Mesh * > mesh_split
Definition gslib.hpp:121
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals, const Array< IntegrationRule * > *ir_in=nullptr, bool by_element=false) const
Get GridFunction value at the points expected by GSLIB.
Definition gslib.cpp:3364
virtual void InterpolateGeneral(const GridFunction &field_in, Vector &field_out, const int field_out_ordering)
Definition gslib.cpp:4073
virtual void InterpolateH1(const GridFunction &field_in, Vector &field_out, const int field_out_ordering)
Definition gslib.cpp:3993
void FindPoints(const Vector &point_pos, int point_pos_ordering=Ordering::byNODES)
Searches positions given in physical space by point_pos.
Definition gslib.cpp:1372
void Setup(Mesh &m, const double bbox_rel_size_inc=0.1, const double newt_tol=1.0e-12, const int npt_max=256)
Preprocess the internal mesh in gslib.
Definition gslib.cpp:321
void FindPointsSurfSetup3(DevStruct &devs, const double *const elx[3], const unsigned n, const unsigned int nel, const unsigned m, const double bbox_rel_size_inc, const unsigned int local_hash_size, const unsigned int global_hash_size, const int rD, const Vector *aabb_sz_inc)
Preprocess 3D surface mesh needed for FindPoints.
Definition gslib.cpp:1003
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.
void SetupSurf(Mesh &m, const double bbox_rel_size_inc=0.1, const double newt_tol=1.0e-12)
Preprocess the surface mesh to compute data for FindPoints.
Definition gslib.cpp:1193
Array< int > split_element_geom
Definition gslib.hpp:146
virtual void Interpolate(const GridFunction &field_in, Vector &field_out)
Interpolation of field values at prescribed reference space positions.
Definition gslib.cpp:3679
Array< FiniteElementSpace * > fes_rst_map
Definition gslib.hpp:130
Mesh * GetBoundingBoxMesh(int type)
Return the bounding boxes as a mesh on rank 0.
Definition gslib.cpp:4474
Array< int > split_element_index
Definition gslib.hpp:144
FiniteElementCollection * fec_map_lin
Definition gslib.hpp:132
FindPointsGSLIB()
Serial constructor.
Definition gslib.cpp:200
virtual void DistributeInterpolatedValues(const Vector &int_vals, const int vdim, const int ordering, Vector &field_out) const
Definition gslib.cpp:4317
struct gslib::comm * gsl_comm
Definition gslib.hpp:135
Array< unsigned int > gsl_elem
Definition gslib.hpp:137
Array< unsigned int > recv_proc
Definition gslib.hpp:139
virtual void SetupSplitMeshesAndIntegrationRules(const int order)
Helper function that calls SetupSplitMeshes and SetupIntegrationRules.
Definition gslib.cpp:3316
Array< unsigned int > gsl_proc
Definition gslib.hpp:137
virtual void SetupIntegrationRuleForSplitMesh(Mesh *mesh, IntegrationRule *irule, int order)
Setup integration points that will be used to interpolate the nodal location at points expected by GS...
Definition gslib.cpp:3222
void InterpolateLocal3(const Vector &field_in, Array< int > &gsl_elem_dev_l, Vector &gsl_ref_l, Vector &field_out, int npt, int ncomp, int dof1dsol)
Interpolate on device for 3D.
Array< unsigned int > gsl_mfem_elem
Definition gslib.hpp:137
Array< IntegrationRule * > ir_split
Definition gslib.hpp:124
void FindPointsEdgeLocal3(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 edge elements.
void GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC, Vector &obbV) const
Definition gslib.cpp:4589
double default_interp_value
Definition gslib.hpp:141
void FindPointsEdgeSetup2(DevStruct &devs, const double *const elx[2], const unsigned n, const unsigned int nel, const unsigned m, const double bbox_rel_size_inc, const unsigned int local_hash_size, const unsigned int global_hash_size, const Vector *aabb_sz_inc)
Preprocess 2D surface mesh needed for FindPoints.
Definition gslib.cpp:1093
Array< IntegrationRule * > ir_split_sol
Definition gslib.hpp:127
virtual void SetupSplitMeshes()
Since GSLIB is designed to work with quads/hexes, we split every triangle/tet/prism/pyramid element i...
Definition gslib.cpp:3026
virtual void MapRefPosAndElemIndices()
Map {r,s,t} coordinates from [-1,1] to [0,1] for MFEM. For simplices, find the original element numbe...
Definition gslib.cpp:3502
virtual void FreeData()
Cleans up memory allocated internally by gslib.
Definition gslib.cpp:2984
virtual void InterpolateSurf(const GridFunction &field_in, Vector &field_out)
Same as Interpolate but for surface meshes.
Definition gslib.cpp:3873
Array< unsigned int > GetPointsNotFoundIndices() const
Get array of indices of not-found points.
Definition gslib.cpp:4244
void InterpolateLocal1(const Vector &field_in, Array< int > &gsl_elem_dev_l, Vector &gsl_ref_l, Vector &field_out, int npt, int ncomp, int dof1dsol)
Interpolate on device for 1D.
void SetupSurfWithAABBExpansion(Mesh &m, const Vector &aabb_sz_inc, const double newt_tol=1.0e-12)
Preprocess the surface mesh to compute data for FindPoints using absolute AABB expansion.
Definition gslib.cpp:1200
Array< GridFunction * > gf_rst_map
Definition gslib.hpp:131
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.
Array< int > split_element_map
Definition gslib.hpp:143
void SetupSurfBase(Mesh &m, const double bbox_rel_size_inc, const Vector *aabb_sz_inc, const double newt_tol)
Shared implementation for the public surface-setup methods.
Definition gslib.cpp:1207
void FindPointsSurf(const Vector &point_pos, int point_pos_ordering=Ordering::byNODES)
Searches positions given in physical space by point_pos on surface mesh.
Definition gslib.cpp:2235
void InterpolateOnDevice(const Vector &field_in_evec, Vector &field_out, const int nel, const int ncomp, const int dof1dsol, const int ordering)
Interpolation of field values at prescribed reference space positions.
Definition gslib.cpp:1988
Array< unsigned int > recv_index
Definition gslib.hpp:139
struct mfem::FindPointsGSLIB::DevStruct DEV
int ir_split_sol_order
Order at which ir_split_sol was built; -1 means not built.
Definition gslib.hpp:129
void InterpolateLocal2(const Vector &field_in, Array< int > &gsl_elem_dev_l, Vector &gsl_ref_l, Vector &field_out, int npt, int ncomp, int dof1dsol)
Interpolate on device for 2D.
void FindPointsLocal2(const Vector &point_pos, int point_pos_ordering, Array< unsigned int > &gsl_code_dev_l, Array< unsigned int > &gsl_elem_dev_l, Vector &gsl_ref_l, Vector &gsl_dist_l, int npt)
FindPoints locally on device for 2D.
virtual void SetupIntegrationRules(const int order, Array< IntegrationRule * > &ir_out)
Build integration rules at the given order for each split mesh and store them in ir_out....
Definition gslib.cpp:3273
void SetupDevice()
Prepare data for device execution for volume meshes.
Definition gslib.cpp:1507
struct gslib::crystal * cr
Definition gslib.hpp:134
void InterpolateSurfBase(const Vector &field_in, Vector &field_out, const int nel, const int ncomp, const int dof1dsol, const int field_out_ordering)
Interpolation of field values at prescribed reference space positions for surface meshes.
Definition gslib.cpp:2730
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
Definition fe_coll.hpp:27
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition fespace.hpp:210
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
Definition fespace.hpp:673
DofTransformation * GetElementVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom for the i'th element. The returned indices are offsets into an ...
Definition fespace.cpp:299
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
Definition fespace.cpp:3860
Ordering::Type GetOrdering() const
Return the ordering method.
Definition fespace.hpp:852
const ElementRestrictionOperator * GetElementRestriction(ElementDofOrdering e_ordering) const
Return an Operator that converts L-vectors to E-vectors.
Definition fespace.cpp:1476
virtual void Update(bool want_transform=true)
Reflect changes in the mesh: update number of DOFs, etc. Also, calculate GridFunction transformation ...
Definition fespace.cpp:4192
int GetElementOrder(int i) const
Returns the order of the i'th finite element.
Definition fespace.cpp:195
void SetElementOrder(int i, int p)
Sets the order of the i'th finite element.
Definition fespace.cpp:170
const FiniteElementCollection * FEColl() const
Definition fespace.hpp:854
Mesh * GetMesh() const
Returns the mesh.
Definition fespace.hpp:639
int GetVDim() const
Returns the vector dimension of the finite element space.
Definition fespace.hpp:817
const FiniteElement * GetTypicalFE() const
Return GetFE(0) if the local mesh is not empty; otherwise return a typical FE based on the Geometry t...
Definition fespace.cpp:3896
virtual int GetMaxElementOrder() const
Return the maximum polynomial order over all elements.
Definition fespace.hpp:669
Abstract class for all finite elements.
Definition fe_base.hpp:294
int GetOrder() const
Returns the order of the finite element. In the case of anisotropic orders, returns the maximum order...
Definition fe_base.hpp:414
Geometry::Type GetGeomType() const
Returns the Geometry::Type of the reference element.
Definition fe_base.hpp:407
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition fe_base.hpp:410
struct gslib::comm * gsl_comm
Definition gslib.hpp:795
void GS(Vector &senddata, GSOp op)
Definition gslib.cpp:4965
GSOPGSLIB(Array< long long > &ids)
Definition gslib.cpp:4908
virtual ~GSOPGSLIB()
Definition gslib.cpp:4935
GSOp
Supported operation types. See class description.
Definition gslib.hpp:809
struct gslib::crystal * cr
Definition gslib.hpp:794
struct gslib::gs_data * gsl_data
Definition gslib.hpp:796
void UpdateIdentifiers(const Array< long long > &ids)
Definition gslib.cpp:4949
static bool CheckPoint(int GeomType, const IntegrationPoint &ip)
Check if the given point is inside the given reference element.
Definition geom.cpp:435
Class to map a point in physical space to candidate ranks.
Definition gslib.hpp:837
const Array< int > & GetGridMap() const
Definition gslib.hpp:906
const Vector & GetGridFac() const
Return the number of grid cells per unit extent in each direction.
Definition gslib.hpp:908
const Vector & GetGridMin() const
Return the minimum extent of the grid in each direction.
Definition gslib.hpp:910
GlobalBBoxTensorGridMap(ParMesh &pmesh, int nx)
Constructor for a given mesh and number of tensor grid divisions.
Definition gslib.cpp:5003
void MapPointsToProcs(Vector &xyz, int ordering, std::map< int, std::vector< int > > &pt_to_procs) const
Get list of procs corresponding to the list of points.
Definition gslib.cpp:5312
const Array< int > & GetGridN() const
Return the grid resolution (number of cells) in each direction.
Definition gslib.hpp:914
Class for grid function - Vector with associated FE space.
Definition gridfunc.hpp:53
virtual real_t GetValue(int i, const IntegrationPoint &ip, int vdim=1) const
Definition gridfunc.cpp:429
FiniteElementSpace * FESpace()
int VectorDim() const
Shortcut for calling FiniteElementSpace::GetVectorDim() on the underlying fes.
Definition gridfunc.hpp:166
virtual void GetVectorValue(int i, const IntegrationPoint &ip, Vector &val) const
Definition gridfunc.cpp:454
Arbitrary order H1-conforming (continuous) finite elements.
Definition fe_coll.hpp:291
int GetBasisType() const
Definition fe_coll.hpp:317
Class for integration point with weight.
Definition intrules.hpp:35
void Set2(const real_t x1, const real_t x2)
Definition intrules.hpp:59
void Set(const real_t x1, const real_t x2, const real_t x3, const real_t w)
Definition intrules.hpp:68
void Set3(const real_t x1, const real_t x2, const real_t x3)
Definition intrules.hpp:57
Class for an integration rule - an Array of IntegrationPoint.
Definition intrules.hpp:96
int GetNPoints() const
Returns the number of the points in the integration rule.
Definition intrules.hpp:255
IntegrationPoint & IntPoint(int i)
Returns a reference to the i-th integration point.
Definition intrules.hpp:258
Arbitrary order "L2-conforming" discontinuous finite elements.
Definition fe_coll.hpp:369
Mesh data type.
Definition mesh.hpp:67
Element::Type GetElementType(int i) const
Returns the type of element i.
Definition mesh.cpp:8445
static Mesh MakeCartesian1D(int n, real_t sx=1.0)
Creates 1D mesh, divided into n equal intervals.
Definition mesh.cpp:4768
void GetElementVertices(int i, Array< int > &v) const
Returns the indices of the vertices of element i.
Definition mesh.hpp:1622
int AddQuad(int v1, int v2, int v3, int v4, int attr=1)
Adds a quadrilateral to the mesh given by 4 vertices v1 through v4.
Definition mesh.cpp:2164
const FiniteElementSpace * GetNodalFESpace() const
Definition mesh.cpp:7206
const Element * GetElement(int i) const
Return pointer to the i'th element object.
Definition mesh.hpp:1447
int AddVertex(real_t x, real_t y=0.0, real_t z=0.0)
Definition mesh.cpp:2079
int GetNE() const
Returns number of elements.
Definition mesh.hpp:1390
int Dimension() const
Dimension of the reference space used within the elements.
Definition mesh.hpp:1314
void FinalizeHexMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a hexahedral Mesh.
Definition mesh.cpp:3624
void FinalizeQuadMesh(int generate_edges=0, int refine=0, bool fix_orientation=true)
Finalize the construction of a quadrilateral Mesh.
Definition mesh.cpp:2610
int SpaceDimension() const
Dimension of the physical space containing the mesh.
Definition mesh.hpp:1317
static Mesh MakeCartesian3D(int nx, int ny, int nz, Element::Type type, real_t sx=1.0, real_t sy=1.0, real_t sz=1.0, bool sfc_ordering=true)
Creates a mesh for the parallelepiped [0,sx]x[0,sy]x[0,sz], divided into nx*ny*nz hexahedra if type =...
Definition mesh.cpp:4786
void GetNodes(Vector &node_coord) const
Definition mesh.cpp:10112
real_t GetElementVolume(int i)
Definition mesh.cpp:125
int AddHex(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8, int attr=1)
Adds a hexahedron to the mesh given by 8 vertices v1 through v8.
Definition mesh.cpp:2227
static Mesh MakeCartesian2D(int nx, int ny, Element::Type type, bool generate_edges=false, real_t sx=1.0, real_t sy=1.0, bool sfc_ordering=true)
Creates mesh for the rectangle [0,sx]x[0,sy], divided into nx*ny quadrilaterals if type = QUADRILATER...
Definition mesh.cpp:4776
int GetNumGeometries(int dim) const
Return the number of geometries of the given dimension present in the mesh.
Definition mesh.cpp:8014
const real_t * GetVertex(int i) const
Return pointer to vertex i's coordinates.
Definition mesh.hpp:1429
static bool IsFinalized()
Return true if MPI has been finalized.
static bool IsInitialized()
Return true if MPI has been initialized.
static void Init(int &argc, char **&argv, int required=default_thread_required, int *provided=nullptr)
Singleton creation with Mpi::Init(argc, argv).
Abstract operator.
Definition operator.hpp:27
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition operator.hpp:68
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
void Interpolate(const Vector &point_pos, const Array< unsigned int > &point_id, const GridFunction &field_in, Vector &field_out, const int point_pos_ordering=Ordering::byNODES)
Definition gslib.cpp:4898
void Setup(Mesh &m, const int meshid, GridFunction *gfmax=nullptr, const double bbox_rel_size_inc=0.1, const double newt_tol=1.0e-12, const int npt_max=256)
Definition gslib.cpp:4742
void FindPoints(const Vector &point_pos, const Array< unsigned int > &point_id, const int point_pos_ordering=Ordering::byNODES)
Definition gslib.cpp:4818
Abstract parallel finite element space.
Definition pfespace.hpp:31
Class for parallel meshes.
Definition pmesh.hpp:35
MPI_Comm GetComm() const
Definition pmesh.hpp:403
const Array< int > & GetDofMap() const
Get an Array<int> that maps lexicographically ordered indices to the indices of the respective nodes/...
Definition fe_base.hpp:1353
Vector coefficient defined by a vector GridFunction.
Vector data type.
Definition vector.hpp:82
virtual const real_t * HostRead() const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:524
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
void SetDataAndSize(real_t *d, int s)
Set the Vector data and size.
Definition vector.hpp:191
real_t Max() const
Returns the maximal element of the vector.
Definition vector.cpp:1200
void Destroy()
Destroy a vector.
Definition vector.hpp:722
int Size() const
Returns the size of the vector.
Definition vector.hpp:234
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
Definition vector.hpp:145
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:633
virtual real_t * HostWrite()
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:532
void NewDataAndSize(real_t *d, int s)
Set the Vector data and size, deleting the old data, if owned.
Definition vector.hpp:197
real_t * GetData() const
Return a pointer to the beginning of the Vector data.
Definition vector.hpp:243
virtual real_t * HostReadWrite()
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), false).
Definition vector.hpp:540
real_t Min() const
Returns the minimal element of the vector.
Definition vector.cpp:1154
void GetSubVector(const Array< int > &dofs, Vector &elemvect) const
Extract entries listed in dofs to the output Vector elemvect.
Definition vector.cpp:676
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Definition vector.hpp:528
int dim
Definition ex24.cpp:53
int index(int i, int j, int nx, int ny)
Definition life.cpp:236
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
mfem::real_t real_t
int wsize
ulong hash_index_2(const gslib::hash_data_2 *p, const double x[2])
Definition gslib.cpp:1500
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
ulong hash_index_1(double low, double fac, ulong n, double x)
Definition gslib.cpp:1483
ulong hash_index_3(const gslib::hash_data_3 *p, const double x[3])
Definition gslib.cpp:1490
void obboxsurf_calc_3(Vector &bb, const double *const elx[3], const unsigned n, uint nel, const unsigned m, const double tol, const bool store_obb)
Definition gslib.cpp:505
ulong hash_index_nd(const Vector &hash_min, const Vector &hash_fac, const int n, const Vector &x)
Definition gslib.cpp:2214
double dbl_range_diag_expand_2(struct gslib::dbl_range *b, double tol)
Definition gslib.cpp:386
double dbl_range_diag_expand_3(struct gslib::dbl_range *b, double tol)
Definition gslib.cpp:398
void CalcInverse(const DenseMatrix &a, DenseMatrix &inva)
slong lfloor(double x)
Definition gslib.cpp:1480
void obboxedge_calc_2(Vector &bb, const double *const elx[2], const unsigned nr, uint nel, const unsigned mr, const double tol, const bool store_obb)
Definition gslib.cpp:737
const T & AsConst(const T &a)
Utility function similar to std::as_const in c++17.
Definition array.hpp:453
float real_t
Definition config.hpp:46
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
Definition fespace.hpp:49
void forall(int N, lambda &&body)
Definition forall.hpp:1134
void obboxedge_calc_3(Vector &bb, const double *const elx[3], const unsigned nr, uint nel, const unsigned mr, const double tol, const bool store_obb)
Definition gslib.cpp:841
real_t p(const Vector &x, real_t t)
struct gslib::hash_data_3 * hash3
Definition gslib.hpp:169
Array< unsigned int > lh_offset
Definition gslib.hpp:172
struct gslib::crystal * cr
Definition gslib.hpp:168
Array< unsigned int > gh_offset
Definition gslib.hpp:172
struct gslib::hash_data_2 * hash2
Definition gslib.hpp:170
std::array< int, NCMesh::MaxFaceNodes > nodes