Nektar++
ProcessWallNormalData.cpp
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////////////
2//
3// File: ProcessWallNormalData.cpp
4//
5// For more information, please see: http://www.nektar.info/
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: Get the wall-normal data at a given origin.
32//
33////////////////////////////////////////////////////////////////////////////////
34
35#include <iostream>
36#include <string>
37
42
44
45using namespace std;
46
47namespace Nektar::FieldUtils
48{
49
52 ModuleKey(eProcessModule, "wallNormalData"),
54 "Export data in wall-normal direction from a single point on the "
55 "wall.");
56
59{
60 f->m_writeBndFld = false; // turned on in upstream ProcessBoundaryExtract
61
62 m_config["xorig"] =
63 ConfigOption(false, "0.5,0.0,0.0",
64 "The point to be projected onto the wall to get the \
65 sampling origin. default=[0.5,0,0]");
66 m_config["projDir"] =
67 ConfigOption(false, "0.0,1.0,0.0",
68 "The direction to project the point onto the wall to \
69 get the sampling origin. default=[0,1,0]");
70 m_config["maxDist"] = ConfigOption(
71 false, "1.0", "Distance to limit projection distance to find the \
72 desired sampling origin. defalut=1.0");
73 m_config["distH"] = ConfigOption(
74 false, "0.01", "Sampling distance along the wall normal at the \
75 sampling origin. default=0.1");
76 m_config["nptsH"] = ConfigOption(
77 false, "5", "Number of sampling points along the wall normal. \
78 default=5");
80 false, "0.1", "The parameter that controls the sampling points' \
81 distribution. d should be in the range (0,inf). d \
82 in (0,0.95] gives controled points; d in (0.95,inf) \
83 gives evenly spaced points");
84
85 // To allow some functions called somewhere else outside Process(),
86 // m_spacedim should be defined while it cannot be defined here in the
87 // constructor because f->m_exp[0]->GetCoordim(0) and
88 // f->m_graph->GetMeshDimension() has not been defeind yet
89 // *It is defeind in m_f->SetUpExp(vm), where vm is necessary.
90}
91
93{
94}
95
96/*Note
97 * This module is used to get field data in the wall-normal direction.
98 * The input cases can be 2D, 2.5D and 3D.
99 * The data will be exported with .pts extension.
100 *
101 * The user defined parameters are: bnd, xorig, searchDir, maxDist, distH,
102 * nptsH, and d.
103 * - bnd=0 is the boundary id. This boundary should contain the desired origin.
104 * - xorig="x,y,z" are the coordinates of the input sampling origin. They are
105 * used as the references to get exact origin on the wall.
106 * - projDir="0,1,0" is the projection direction to find the point on the wall
107 * - maxDist=1.0 is the distance that constrains the projection on the wall.
108 * The projection distance should be smaller than this distance. This
109 * parameter is set in case the boundary is wavey so that multiple
110 * projections exist at the same time.
111 * - distH=0.01 is the sampling depth in the wall-normal direction.
112 * - nptsH=11 is the number of sampling points along wall-normal direction.
113 * - d=0.1 is a destribution control parameter of the sampling points. It
114 * should be in the range (0,inf). d in range (0,0.95] for controlled array.
115 * d>0.95 gives evenly spaced array.
116 */
117void ProcessWallNormalData::v_Process(po::variables_map &vm)
118{
120
121 // Get dim to store data
122 const int nfields = m_f->m_variables.size();
123 const int nCoordDim = m_f->m_exp[0]->GetCoordim(0);
124 const int nBndLcoordDim = nCoordDim - 1;
125 m_spacedim = m_f->m_exp[0]->GetCoordim(0) + m_f->m_numHomogeneousDir;
126 const int totVars = m_spacedim + nfields;
127
128 // Initialize the sampling parameters
129 std::vector<NekDouble> xorig, searchDir;
130 ASSERTL0(ParseUtils::GenerateVector(m_config["xorig"].as<string>(), xorig),
131 "Failed to interpret origin coordinates string");
132 ASSERTL0(
133 ParseUtils::GenerateVector(m_config["projDir"].as<string>(), searchDir),
134 "Failed to interpret search direction string");
135 const NekDouble maxDist = m_config["maxDist"].as<NekDouble>();
136 const NekDouble distH = m_config["distH"].as<NekDouble>();
137 const int nptsH = m_config["nptsH"].as<int>();
138 const NekDouble delta = m_config["d"].as<NekDouble>();
139
140 Array<OneD, NekDouble> orig(3), projDir(3); // gloCoord of the origin
141 for (int i = 0; i < 3; ++i)
142 {
143 orig[i] = (xorig.size() >= (i + 1)) ? xorig[i] : 0.0;
144 projDir[i] = (searchDir.size() >= (i + 1)) ? searchDir[i] : 0.0;
145 }
146 if (nCoordDim == 2)
147 {
148 projDir[2] = 0.0;
149 }
150 Vmath::Smul(3, 1.0 / sqrt(Vmath::Dot(3, projDir, 1, projDir, 1)), projDir,
151 1, projDir, 1);
152
153 // Update z according to the closest plane in 2.5D cases
154 if (m_f->m_numHomogeneousDir == 1)
155 {
156 int nPlanes = m_f->m_exp[0]->GetHomogeneousBasis()->GetZ().size();
157 NekDouble lHom = m_f->m_exp[0]->GetHomoLen();
158 if (orig[2] < 0.0 || orig[2] > lHom)
159 {
160 orig[2] = 0.0;
161 }
162 else
163 {
164 NekDouble dZ = lHom / nPlanes;
165 NekDouble zTmp, zCur = 0.0, distTmp, distCur = 999.0;
166 for (int i = 0; i <= nPlanes; ++i)
167 {
168 zTmp = dZ * i;
169 distTmp = fabs(orig[2] - zTmp);
170 if (distTmp < distCur)
171 {
172 distCur = distTmp;
173 zCur = zTmp;
174 if (i == nPlanes)
175 {
176 zCur = 0.0;
177 }
178 }
179 }
180 orig[2] = zCur;
181 }
182 }
183
184 // Get the bnd id
186 m_f->m_exp[0]->GetGraph());
188 bcs.GetBoundaryRegions();
189 map<int, int> BndRegionMap;
190 int cnt = 0;
191 for (auto &breg_it : bregions)
192 {
193 BndRegionMap[breg_it.first] = cnt++;
194 }
195 int bnd = BndRegionMap[m_f->m_bndRegionsToWrite[0]];
196
197 // Get expansion list for boundary
199 for (int i = 0; i < nfields; ++i)
200 {
201 BndExp[i] = m_f->m_exp[i]->UpdateBndCondExpansion(bnd);
202 }
203
204 //-------------------------------------------------------------------------
205 // Find the element that contains the origin, and give the precise origin
207 Array<OneD, NekDouble> locCoord(nBndLcoordDim, -999.0);
208 NekDouble projDist;
209 int elmtid;
210 bool isInside = false;
211
212 // Search and get precise locCoord
213 const int nElmts = BndExp[0]->GetNumElmts();
214 for (elmtid = 0; elmtid < nElmts; ++elmtid) // nElmts
215 {
216 bndGeom = BndExp[0]->GetExp(elmtid)->GetGeom();
217 isInside = BndElmtContainsPoint(bndGeom, orig, projDir, locCoord,
218 projDist, maxDist);
219 if (isInside)
220 {
221 break;
222 }
223 }
224 ASSERTL0(isInside, "Failed to find the sampling origin on the boundary.");
225
226 // Then Update the precise sampling position
227 StdRegions::StdExpansionSharedPtr bndXmap = bndGeom->GetXmap();
228 const int npts = bndXmap->GetTotPoints();
230 Array<OneD, Array<OneD, const NekDouble>> bndCoeffs(nCoordDim);
231
232 for (int i = 0; i < nCoordDim; ++i)
233 {
234 pts[i] = Array<OneD, NekDouble>(npts);
235 bndCoeffs[i] = bndGeom->GetCoeffs(i); // 0/1/2 for x/y/z
236 bndXmap->BwdTrans(bndCoeffs[i], pts[i]);
237 orig[i] = bndXmap->PhysEvaluate(locCoord, pts[i]); // Update wall point
238 }
239
240 // Get outward-pointing normal vectors for all quadrature points on bnd
241 // Use these normals as diretion references
243 m_f->m_exp[0]->GetBoundaryNormals(bnd, normalsQ);
244
245 // Get the normals in the element
246 // Set point key to get point id
247 Array<OneD, LibUtilities::PointsKey> from_key(nBndLcoordDim);
248 int from_nPtsPerElmt = 1;
249 for (int i = 0; i < nBndLcoordDim; ++i)
250 {
251 from_key[i] = BndExp[0]->GetExp(elmtid)->GetBasis(i)->GetPointsKey();
252 from_nPtsPerElmt *= from_key[i].GetNumPoints();
253 }
254
255 // Get ref normal
256 Array<OneD, NekDouble> normalsRef(3, 0.0);
257 int refId = elmtid * from_nPtsPerElmt + 0; // the 1st normal in the bnd elmt
258 for (int i = 0; i < m_spacedim; ++i)
259 {
260 normalsRef[i] = normalsQ[i][refId];
261 }
262
263 // Get the precise normals and correct the direction to be inward
264 Array<OneD, NekDouble> normals(3, 0.0);
265 GetNormals(bndGeom, locCoord, normals);
266
267 if (Vmath::Dot(3, normals, normalsRef) > 0.0)
268 {
269 Vmath::Neg(3, normals, 1);
270 }
271
272 // Output the info if -v is set
273 if (m_f->m_verbose)
274 {
275 cout << "------ wallNormalData module ------\n";
276 cout << "Input point:\n";
277 cout << " - [Px,Py,Pz] = [" << xorig[0] << ", " << xorig[1];
278 if (xorig.size() >= 3)
279 {
280 cout << ", " << xorig[2] << "]\n";
281 }
282 else
283 {
284 cout << ", " << 0.0 << "]\n";
285 }
286 cout << "Projection direction:\n";
287 cout << " - [vx,vy,vz] = [" << projDir[0] << ", " << projDir[1] << ", "
288 << projDir[2] << "]\n";
289 cout << "Sampling origin on the wall:\n";
290 cout << " - [Ox,Oy,Oz] = [" << orig[0] << ", " << orig[1] << ", "
291 << orig[2] << "]\n";
292 cout << "Normals at the origin:\n";
293 cout << " - [nx,ny,nz] = [" << normals[0] << ", " << normals[1] << ", "
294 << normals[2] << "]\n";
295 cout
296 << "Ref normals (at quadrature points in the projected element):\n";
297 for (int i = 0; i < from_nPtsPerElmt; ++i)
298 {
299 cout << " - " << i << ": [nx,ny,nz] = ["
300 << -normalsQ[0][elmtid * from_nPtsPerElmt + i] << ", "
301 << -normalsQ[1][elmtid * from_nPtsPerElmt + i];
302 if (m_spacedim == 3)
303 {
304 cout << ", " << -normalsQ[2][elmtid * from_nPtsPerElmt + i]
305 << "]\n";
306 }
307 else
308 {
309 cout << "]\n";
310 }
311 }
312 cout << endl;
313 }
314
315 //-------------------------------------------------------------------------
316 // Set the depth of sampling array
317 Array<OneD, NekDouble> h(nptsH, 0.0);
318 if (delta > 0 && delta <= 0.95)
319 {
320 // Use expression in Agrawal's paper:
321 // h = 1-tanh((1-ksi)*atanh(sqrt(1-delta)))/sqrt(1-delta), ksi in [0,1]
322 NekDouble tmp1;
323 const NekDouble tmp2 = 1.0 / (static_cast<NekDouble>(nptsH) - 1.0);
324 const NekDouble tmp3 = sqrt(1.0 - delta);
325 const NekDouble tmp4 = atanh(tmp3);
326 const NekDouble tmp5 = 1.0 / tmp3;
327 for (int i = 1; i < nptsH; ++i)
328 {
329 tmp1 = 1.0 - i * tmp2; // tmp1 = 1-ksi
330 h[i] = 1 - tanh(tmp1 * tmp4) * tmp5;
331 h[i] *= distH; // physical distance in normal direction
332 }
333 }
334 else if (delta > 0.95)
335 {
336 // Evenly spaced array
337 const NekDouble tmp1 = 1.0 / (static_cast<NekDouble>(nptsH) - 1.0);
338 for (int i = 1; i < nptsH; ++i)
339 {
340 h[i] = i * tmp1;
341 h[i] *= distH; // physical distance in normal direction
342 }
343 }
344 else
345 {
346 ASSERTL0(false, "Input error. Delta needs to be greater than 0.0.");
347 }
348
349 // Set pts coordinates and interpoate the data
351 for (int i = 0; i < totVars; ++i)
352 {
353 ptsH[i] = Array<OneD, NekDouble>(nptsH, 0.0);
354 }
355
356 for (int i = 0; i < m_spacedim; ++i)
357 {
358 for (int j = 0; j < nptsH; ++j)
359 {
360 ptsH[i][j] = orig[i] + h[j] * normals[i]; // x0+dist*nx
361 }
362 }
363
365 m_spacedim, m_f->m_variables, ptsH, LibUtilities::NullPtsInfoMap);
366
368 interp.Interpolate(m_f->m_exp, m_f->m_fieldPts,
370}
371
372/**
373 * @brief Project a single point along the given direction to a plane
374 * @param gloCoord Global coordinate of the point. size=3.
375 * @param projDir Projection direction, which is also the normal vector of
376 * the target plane. size=3, norm=1.
377 * @param distToOrig The distance from the origin (0,0,0) to the target plane
378 * @param projGloCoord The global coordinate of the projecion result.
379 */
381 const Array<OneD, const NekDouble> &gloCoord,
382 const Array<OneD, const NekDouble> &projDir, const NekDouble distToOrig,
383 Array<OneD, NekDouble> &projGloCoord)
384{
385 NekDouble tmp1;
387
388 tmp1 = Vmath::Dot(3, gloCoord, 1, projDir, 1); // |xn| = x dot n
389 Vmath::Smul(3, distToOrig - tmp1, projDir, 1, tmp2,
390 1); // d_xn = (dist-|xn|) n
391 Vmath::Vadd(3, gloCoord, 1, tmp2, 1, projGloCoord, 1); // x' = x + d_xn
392}
393
394/**
395 * @brief Project the vertices of the elmt along the given direction to a plane
396 * @param pts Global coordinate of the vertices of the elmt. size=2/3.
397 * @param projDir Projection direction, which is also the normal vector of
398 * the target plane. size=3, norm=1.
399 * @param distToOrig The distance from the origin (0,0,0) to the target plane.
400 * @param projPts The global coordinate of the projecion result.
401 */
403 const Array<OneD, const Array<OneD, NekDouble>> &pts,
404 const Array<OneD, const NekDouble> &projDir, const NekDouble distToOrig,
406{
407 const int nCoordDim = pts.size(); // 2 for 2.5D cases
408 const int npts = pts[0].size();
409
410 NekDouble tmp1;
411 Array<OneD, NekDouble> singlePnt(nCoordDim), tmp2(nCoordDim);
412
413 for (int i = 0; i < npts; ++i)
414 {
415 // Get a point
416 for (int j = 0; j < nCoordDim; ++j)
417 {
418 singlePnt[j] = pts[j][i];
419 }
420
421 // Projection
422 tmp1 = Vmath::Dot(nCoordDim, singlePnt, 1, projDir, 1);
423 Vmath::Smul(nCoordDim, distToOrig - tmp1, projDir, 1, tmp2, 1);
424 Vmath::Vadd(nCoordDim, singlePnt, 1, tmp2, 1, singlePnt, 1);
425
426 // Save to projPts
427 for (int j = 0; j < nCoordDim; ++j)
428 {
429 projPts[j][i] = singlePnt[j];
430 }
431 }
432}
433
434/**
435 * @brief Determine if the projected point is inside the projected element.
436 * @param projGloCoord The global coordinate of the projected single point.
437 * @param projPts The global coordinate of the projected vertices,size=2/3
438 * @param projDir Projection direction, which is used as the reference
439 * direction in the 3D routine. size=3, norm=1.
440 * @param paralTol Tolerence to check if two vectors are parallel.
441 * @param angleTol Tolerence to check if the total angle is 2*pi.
442 * @return Inside (true) or not (false)
443 */
445 const Array<OneD, const NekDouble> &projGloCoord,
446 const Array<OneD, const Array<OneD, NekDouble>> &projPts,
447 const NekDouble paralTol)
448{
449 const NekDouble npts = projPts[0].size();
450
451 Array<OneD, NekDouble> vec1(2, 0.0), vec2(2, 0.0);
452
453 vec1[0] = projPts[0][0] - projGloCoord[0];
454 vec1[1] = projPts[1][0] - projGloCoord[1];
455 vec2[0] = projPts[0][npts - 1] - projGloCoord[0];
456 vec2[1] = projPts[1][npts - 1] - projGloCoord[1];
457
458 Vmath::Smul(2, 1.0 / sqrt(Vmath::Dot(2, vec1, 1, vec1, 1)), vec1, 1, vec1,
459 1);
460 Vmath::Smul(2, 1.0 / sqrt(Vmath::Dot(2, vec2, 1, vec2, 1)), vec2, 1, vec2,
461 1);
462
463 if ((fabs(vec1[0] + vec2[0]) + fabs(vec1[1] + vec2[1])) < paralTol)
464 {
465 // On the line segement, true
466 return true;
467 }
468 else
469 {
470 return false;
471 }
472}
473
475 const Array<OneD, const NekDouble> &projGloCoord,
476 const Array<OneD, const Array<OneD, NekDouble>> &projPts,
477 const Array<OneD, const NekDouble> &projDir, const NekDouble paralTol,
478 const NekDouble angleTol)
479{
480
481 // Generate a polygen (quad), re-order the points on the edge
482 const NekDouble npts = projPts[0].size();
483 const int nptsEdge =
484 sqrt(npts); // num of points on edge for geo representation
485 const int nptsPolygon = 4 * nptsEdge - 4;
487 for (int i = 0; i < 3; ++i)
488 {
489 ptsPolygon[i] = Array<OneD, NekDouble>(nptsPolygon);
490
491 for (int j = 0; j < nptsEdge; ++j)
492 {
493 ptsPolygon[i][j] = projPts[i][j];
494 }
495 for (int j = 0; j < nptsEdge - 2; ++j)
496 {
497 ptsPolygon[i][nptsEdge + j] = projPts[i][(j + 2) * nptsEdge - 1];
498 }
499 for (int j = 0; j < nptsEdge; ++j)
500 {
501 ptsPolygon[i][2 * nptsEdge - 2 + j] = projPts[i][npts - 1 - j];
502 }
503 for (int j = 0; j < nptsEdge - 2; ++j)
504 {
505 ptsPolygon[i][3 * nptsEdge - 2 + j] =
506 projPts[i][nptsEdge * (nptsEdge - j - 2)];
507 }
508 }
509
510 // Determine relation using angle method (sum=2*pi)
511 NekDouble angleCos, angleAbs, angleSign, angleSum = 0.0;
512 Array<OneD, NekDouble> vec1(3, 0.0), vec2(3, 0.0), vec3(3, 0.0);
513 int id1, id2;
514
515 for (int i = 0; i < nptsPolygon; ++i)
516 {
517 id1 = i;
518 id2 = (id1 == (nptsPolygon - 1)) ? 0 : (id1 + 1);
519
520 for (int j = 0; j < 3; ++j)
521 {
522 vec1[j] = ptsPolygon[j][id1] - projGloCoord[j];
523 vec2[j] = ptsPolygon[j][id2] - projGloCoord[j];
524 }
525
526 Vmath::Smul(3, 1.0 / sqrt(Vmath::Dot(3, vec1, 1, vec1, 1)), vec1, 1,
527 vec1, 1);
528 Vmath::Smul(3, 1.0 / sqrt(Vmath::Dot(3, vec2, 1, vec2, 1)), vec2, 1,
529 vec2, 1);
530
531 if ((fabs(vec1[0] + vec2[0]) + fabs(vec1[1] + vec2[1]) +
532 fabs(vec1[2] + vec2[2])) < paralTol)
533 {
534 // On the line segement, true
535 // This branch is used to aviod angle=pi but angleSign=-1 (not 1)
536 return true;
537 }
538 else
539 {
540 // Off the line segement, compute angle
541 // vec3 = vec1 x vec2, not scaled
542 vec3[0] = vec1[1] * vec2[2] - vec1[2] * vec2[1];
543 vec3[1] = vec1[2] * vec2[0] - vec1[0] * vec2[2];
544 vec3[2] = vec1[0] * vec2[1] - vec1[1] * vec2[0];
545
546 // Use the projDir as reference direction (positive)
547 angleSign = Vmath::Dot(3, vec3, 1, projDir, 1) > 0.0 ? 1.0 : -1.0;
548 angleCos = Vmath::Dot(3, vec1, 1, vec2, 1);
549
550 // Avoid error in using acos()
551 if (angleCos > 1.0)
552 {
553 angleCos = 1.0;
554 }
555 else if (angleCos < -1.0)
556 {
557 angleCos = -1.0;
558 }
559
560 angleAbs = acos(angleCos);
561 angleSum += angleSign * angleAbs;
562 }
563 }
564
565 // Check
566 angleSum = fabs(angleSum);
567 if (fabs(angleSum - 2.0 * M_PI) < angleTol)
568 {
569 return true;
570 }
571 else
572 {
573 return false;
574 }
575}
576
577/**
578 * @brief Use iteration to get the locCoord. This routine should be used after
579 * we have checked the projected point is inside the projected element.
580 * @param bndGeom Geometry to get the xmap.
581 * @param gloCoord Global coordinate of the point. size=3.
582 * @param pts Global coordinate of the vertices of the elmt. size=2/3.
583 * @param dieUse The main direction(s) used to compute local coordinate
584 * @param locCoord Iteration results for local coordinate(s)
585 * @param dist Returned distance in physical space if the collapsed
586 * locCoord is out of range [-1,1].
587 * @param iterTol Tolerence for iteration.
588 * @param iterMax Maximum iteration steps
589 * @return Converged (true) or not (false)
590 */
593 const Array<OneD, const NekDouble> &gloCoord,
594 const Array<OneD, const Array<OneD, NekDouble>> &pts,
595 const Array<OneD, const int> &dirUse, Array<OneD, NekDouble> &locCoord,
596 const NekDouble iterTol, const int iterMax)
597{
598 // Initial settings
599 Array<OneD, NekDouble> etaLR(2); // range [-1,1]
600 etaLR[0] = -1.0; // left
601 etaLR[1] = 1.0; // right
602 NekDouble tmpL, tmpR; // tmp values for L/R
603
604 StdRegions::StdExpansionSharedPtr bndXmap = bndGeom->GetXmap();
605 locCoord[0] = -2.0;
606 int cnt = 0;
607 bool isConverge = false;
608 while (cnt < iterMax)
609 {
610 tmpL = bndXmap->PhysEvaluate(etaLR, pts[dirUse[0]]);
611 tmpR = bndXmap->PhysEvaluate(etaLR + 1, pts[dirUse[0]]);
612
613 if (fabs(gloCoord[dirUse[0]] - tmpL) >=
614 fabs(gloCoord[dirUse[0]] - tmpR))
615 {
616 etaLR[0] = 0.5 * (etaLR[0] + etaLR[1]);
617 }
618 else
619 {
620 etaLR[1] = 0.5 * (etaLR[0] + etaLR[1]);
621 }
622
623 if ((etaLR[1] - etaLR[0]) < iterTol)
624 {
625 locCoord[0] = 0.5 * (etaLR[0] + etaLR[1]);
626 isConverge = true;
627 break;
628 }
629
630 ++cnt;
631 }
632
633 // Warning if failed
634 if (cnt >= iterMax)
635 {
636 WARNINGL1(false, "Bisection iteration is not converged");
637 }
638
639 return isConverge;
640}
641
644 const Array<OneD, const NekDouble> &gloCoord,
645 const Array<OneD, const Array<OneD, NekDouble>> &pts,
646 const Array<OneD, const int> &dirUse, Array<OneD, NekDouble> &locCoord,
647 NekDouble &dist, const NekDouble iterTol, const int iterMax)
648{
649
650 const NekDouble LcoordDiv = 15.0;
651
652 StdRegions::StdExpansionSharedPtr bndXmap = bndGeom->GetXmap();
653
655 bndGeom->GetMetricInfo()->GetJac(bndXmap->GetPointsKeys());
656 NekDouble scaledTol =
657 Vmath::Vsum(Jac.size(), Jac, 1) / ((NekDouble)Jac.size());
658 scaledTol *= iterTol;
659
660 // Set the gloCoord used to compute locCoord
661 const int dir1 = dirUse[0];
662 const int dir2 = dirUse[1];
663
664 Array<OneD, NekDouble> Dx1D1(pts[dir1].size());
665 Array<OneD, NekDouble> Dx1D2(pts[dir1].size());
666 Array<OneD, NekDouble> Dx2D1(pts[dir1].size());
667 Array<OneD, NekDouble> Dx2D2(pts[dir1].size());
668
669 // Ideally this will be stored in m_geomfactors
670 bndXmap->PhysDeriv(pts[dir1], Dx1D1, Dx1D2);
671 bndXmap->PhysDeriv(pts[dir2], Dx2D1, Dx2D2);
672
673 // Initial the locCoord, in [-1,1]
674 locCoord[0] = 0.0;
675 locCoord[1] = 0.0;
676
677 NekDouble x1map, x2map, F1, F2;
678 NekDouble derx1_1, derx1_2, derx2_1, derx2_2, jac;
679 NekDouble resid;
680 int cnt = 0;
681 bool isConverge = false;
682
683 F1 = F2 = 2000; // Starting value of Function
684 resid = sqrt(F1 * F1 + F2 * F2);
685 while (cnt++ < iterMax)
686 {
687 x1map = bndXmap->PhysEvaluate(locCoord, pts[dir1]);
688 x2map = bndXmap->PhysEvaluate(locCoord, pts[dir2]);
689
690 F1 = gloCoord[dir1] - x1map;
691 F2 = gloCoord[dir2] - x2map;
692
693 if (F1 * F1 + F2 * F2 < scaledTol)
694 {
695 resid = sqrt(F1 * F1 + F2 * F2);
696 isConverge = true;
697 break;
698 }
699
700 // Interpolate derivative metric at locCoord
701 derx1_1 = bndXmap->PhysEvaluate(locCoord, Dx1D1);
702 derx1_2 = bndXmap->PhysEvaluate(locCoord, Dx1D2);
703 derx2_1 = bndXmap->PhysEvaluate(locCoord, Dx2D1);
704 derx2_2 = bndXmap->PhysEvaluate(locCoord, Dx2D2);
705
706 jac = derx2_2 * derx1_1 - derx2_1 * derx1_2;
707
708 // Use analytical inverse of derivitives which are
709 // also similar to those of metric factors.
710 locCoord[0] = locCoord[0] + (derx2_2 * (gloCoord[dir1] - x1map) -
711 derx1_2 * (gloCoord[dir2] - x2map)) /
712 jac;
713
714 locCoord[1] = locCoord[1] + (-derx2_1 * (gloCoord[dir1] - x1map) +
715 derx1_1 * (gloCoord[dir2] - x2map)) /
716 jac;
717
718 // locCoord have diverged so stop iteration
719 if (!(std::isfinite(locCoord[0]) && std::isfinite(locCoord[1])))
720 {
721 dist = 1e16;
722 std::ostringstream ss;
723 ss << "nan or inf found in NewtonIterForLocCoordOnProjBndElmt in "
724 "element "
725 << bndGeom->GetGlobalID();
726 WARNINGL1(false, ss.str());
727 return false;
728 }
729 if (fabs(locCoord[0]) > LcoordDiv || fabs(locCoord[1]) > LcoordDiv)
730 {
731 break;
732 }
733 }
734
735 // Check distance for collapsed coordinate
737 bndXmap->LocCoordToLocCollapsed(locCoord, eta);
738
739 if (bndGeom->ClampLocCoords(eta, 0.0))
740 {
741 // calculate the global point corresponding to locCoord
742 x1map = bndXmap->PhysEvaluate(eta, pts[dir1]);
743 x2map = bndXmap->PhysEvaluate(eta, pts[dir2]);
744
745 F1 = gloCoord[dir1] - x1map;
746 F2 = gloCoord[dir2] - x2map;
747
748 dist = sqrt(F1 * F1 + F2 * F2);
749 }
750 else
751 {
752 dist = 0.0;
753 }
754
755 // Warning if failed
756 if (cnt >= iterMax)
757 {
758 Array<OneD, NekDouble> collCoords(2);
759 bndXmap->LocCoordToLocCollapsed(locCoord, collCoords);
760
761 // if coordinate is inside element dump error!
762 if ((collCoords[0] >= -1.0 && collCoords[0] <= 1.0) &&
763 (collCoords[1] >= -1.0 && collCoords[1] <= 1.0))
764 {
765 std::ostringstream ss;
766
767 ss << "Reached MaxIterations (" << iterMax
768 << ") in Newton iteration ";
769 ss << "Init value (" << setprecision(4) << 0 << "," << 0 << ","
770 << ") ";
771 ss << "Fin value (" << locCoord[0] << "," << locCoord[1] << ","
772 << ") ";
773 ss << "Resid = " << resid << " Tolerance = " << sqrt(scaledTol);
774
775 WARNINGL1(cnt < iterMax, ss.str());
776 }
777 }
778
779 return isConverge;
780}
781
782/**
783 * @brief Check if a point can be projected onto an oundary element in a given
784 * direction. If yes, give the local coordinates of the projected point.
785 * we have checked the projected point is inside the projected element.
786 * @param bndGeom Pointer to the geometry of the boundary element.
787 * @param gloCoord Global coordinate of the point. size=3.
788 * @param projDir Projection direction, which is used as the reference
789 * direction in the 3D routine. size=3, norm=1.
790 * @param locCoord Iteration results for local coordinates (if inside).
791 * @param projDist Projection distance betweem the point to the wall point.
792 * @param maxDist Disntance to check if the wall point is desired.
793 * @param iterTol Tolerence for iteration.
794 * @return Inside (true) or not (false)
795 */
798 const Array<OneD, const NekDouble> &gloCoord,
799 const Array<OneD, const NekDouble> &projDir,
800 Array<OneD, NekDouble> &locCoord, NekDouble &projDist,
801 const NekDouble maxDist, const NekDouble iterTol)
802{
803 // Get variables
804 StdRegions::StdExpansionSharedPtr bndXmap = bndGeom->GetXmap();
805 const int npts = bndXmap->GetTotPoints();
806 const int nCoordDim = m_f->m_exp[0]->GetCoordim(0); // =2 for 2.5D cases
807
808 Array<OneD, Array<OneD, const NekDouble>> bndCoeffs(nCoordDim);
810 Array<OneD, Array<OneD, NekDouble>> projPts(nCoordDim);
811 Array<OneD, NekDouble> projGloCoord(3, 0.0);
812
813 for (int i = 0; i < nCoordDim; ++i)
814 {
815 pts[i] = Array<OneD, NekDouble>(npts);
816 projPts[i] = Array<OneD, NekDouble>(npts);
817 bndCoeffs[i] = bndGeom->GetCoeffs(i); // 0/1/2 for x/y/z
818 bndXmap->BwdTrans(bndCoeffs[i], pts[i]);
819 }
820
821 // Project the point and vertices of the element in the input direction
822 ProjectPoint(gloCoord, projDir, 0.0, projGloCoord);
823 ProjectVertices(pts, projDir, 0.0, projPts);
824
825 // Set the main direction(s) and the minor direction
826 // The gloCoord for minor direction will not be used for locCoord iteration
827 // dirUse[0] is the main dir for 2D/2.5D cases, dirUse[1]/[2] is the minor
828 // one dirUse[0] and dirUse[1] are the main dir for 3D cases, dirUse[2] is
829 // hte minor one
830 int dirMaxId = 0; // id to get the dir with largest projDir component
831 for (int i = 1; i < nCoordDim; ++i)
832 {
833 if (fabs(projDir[i]) > fabs(projDir[dirMaxId]))
834 {
835 dirMaxId = i;
836 }
837 }
838
839 Array<OneD, int> dirUse(3, 0);
840 if (nCoordDim == 2)
841 {
842 // 2D or 2.5D cases
843 if (dirMaxId == 0)
844 {
845 dirUse[0] = 1;
846 dirUse[1] = 0;
847 dirUse[2] = 2;
848 }
849 else
850 {
851 dirUse[0] = 0;
852 dirUse[1] = 1;
853 dirUse[2] = 2;
854 }
855 }
856 else
857 {
858 // 3D cases
859 if (dirMaxId == 0)
860 {
861 dirUse[0] = 1;
862 dirUse[1] = 2;
863 dirUse[2] = 0;
864 }
865 else if (dirMaxId == 1)
866 {
867 dirUse[0] = 2;
868 dirUse[1] = 0;
869 dirUse[2] = 1;
870 }
871 else
872 {
873 dirUse[0] = 0;
874 dirUse[1] = 1;
875 dirUse[2] = 2;
876 }
877 }
878
879 // Check if the projected point is in the projected elmt
880 // If yes, then compute the locCoord and check if desired point is found
881 if (nCoordDim == 2)
882 {
883 if (isInProjectedArea2D(projGloCoord, projPts, 1.0e-12))
884 {
885 bool isConverge, isDesired;
886
888 bndGeom, projGloCoord, projPts, dirUse, locCoord, iterTol);
889
890 Array<OneD, NekDouble> tmp(2, 0.0);
891 tmp[0] = bndXmap->PhysEvaluate(locCoord, pts[0]) - gloCoord[0];
892 tmp[1] = bndXmap->PhysEvaluate(locCoord, pts[1]) - gloCoord[1];
893 projDist = Vmath::Dot(2, tmp, 1, projDir, 1); // can be negative
894
895 isDesired = (projDist > 0.0) && (projDist < maxDist);
896
897 return isConverge && isDesired;
898 }
899 else
900 {
901 return false;
902 }
903 }
904 else
905 {
906 if (isInProjectedArea3D(projGloCoord, projPts, projDir, 1.0e-12,
907 1.0e-6))
908 {
909 NekDouble dist;
910 bool isConverge, isDesired;
911
912 isConverge =
913 NewtonIterForLocCoordOnBndElmt(bndGeom, projGloCoord, projPts,
914 dirUse, locCoord, dist, iterTol);
915
916 if (dist > iterTol)
917 {
918 std::ostringstream ss;
919 ss << "Collapsed locCoord out of range.\n"
920 << "Newton iteration gives the distance: " << dist;
921 WARNINGL1(false, ss.str());
922 }
923
924 Array<OneD, NekDouble> tmp(3, 0.0);
925 tmp[0] = bndXmap->PhysEvaluate(locCoord, pts[0]) - gloCoord[0];
926 tmp[1] = bndXmap->PhysEvaluate(locCoord, pts[1]) - gloCoord[1];
927 tmp[2] = bndXmap->PhysEvaluate(locCoord, pts[2]) - gloCoord[2];
928 projDist = Vmath::Dot(3, tmp, 1, projDir, 1); // can be negative
929
930 isDesired = (projDist > 0.0) && (projDist < maxDist);
931
932 return isConverge && isDesired;
933 }
934 else
935 {
936 return false;
937 }
938 }
939}
940
941/**
942 * @brief Get the normals for a given locCoord
943 * @param bndGeom Pointer to the geometry of the boundary element.
944 * @param locCoord Iteration results for local coordinates (if inside).
945 * @param normals Wall normal as the result
946 */
949 const Array<OneD, const NekDouble> &locCoord,
950 Array<OneD, NekDouble> &normals)
951{
952 const int nCoordDim = m_f->m_exp[0]->GetCoordim(0); // =2 for 2.5D cases
953 StdRegions::StdExpansionSharedPtr bndXmap = bndGeom->GetXmap();
956 int npts = bndXmap->GetTotPoints();
957
958 // Get pts in the element
959 for (int i = 0; i < nCoordDim; ++i)
960 {
961 pts[i] = Array<OneD, NekDouble>(npts);
962 bndCoeffs[i] = bndGeom->GetCoeffs(i); // 0/1/2 for x/y/z
963 bndXmap->BwdTrans(bndCoeffs[i], pts[i]);
964 }
965
966 // Get the outward-pointing normals according to the given locCoord
967 if (nCoordDim == 2)
968 {
969 Array<OneD, NekDouble> DxD1(pts[0].size());
970 Array<OneD, NekDouble> DyD1(pts[0].size());
971
972 bndXmap->PhysDeriv(pts[0], DxD1);
973 bndXmap->PhysDeriv(pts[1], DyD1);
974
975 NekDouble dxd1, dyd1, norm;
976 dxd1 = bndXmap->PhysEvaluate(locCoord, DxD1);
977 dyd1 = bndXmap->PhysEvaluate(locCoord, DyD1);
978 norm = sqrt(dxd1 * dxd1 + dyd1 * dyd1);
979
980 normals[0] = dyd1 / norm;
981 normals[1] = -dxd1 / norm;
982 normals[2] = 0.0;
983 }
984 else
985 {
986 Array<OneD, NekDouble> DxD1(pts[0].size());
987 Array<OneD, NekDouble> DxD2(pts[0].size());
988 Array<OneD, NekDouble> DyD1(pts[0].size());
989 Array<OneD, NekDouble> DyD2(pts[0].size());
990 Array<OneD, NekDouble> DzD1(pts[0].size());
991 Array<OneD, NekDouble> DzD2(pts[0].size());
992
993 bndXmap->PhysDeriv(pts[0], DxD1, DxD2);
994 bndXmap->PhysDeriv(pts[1], DyD1, DyD2);
995 bndXmap->PhysDeriv(pts[2], DzD1, DzD2);
996
997 NekDouble dxd1, dyd1, dzd1, dxd2, dyd2, dzd2;
998 dxd1 = bndXmap->PhysEvaluate(locCoord, DxD1);
999 dxd2 = bndXmap->PhysEvaluate(locCoord, DxD2);
1000 dyd1 = bndXmap->PhysEvaluate(locCoord, DyD1);
1001 dyd2 = bndXmap->PhysEvaluate(locCoord, DyD2);
1002 dzd1 = bndXmap->PhysEvaluate(locCoord, DzD1);
1003 dzd2 = bndXmap->PhysEvaluate(locCoord, DzD2);
1004
1005 NekDouble n1, n2, n3, norm;
1006 n1 = dyd1 * dzd2 - dyd2 * dzd1;
1007 n2 = dzd1 * dxd2 - dzd2 * dxd1;
1008 n3 = dxd1 * dyd2 - dxd2 * dyd1;
1009 norm = sqrt(n1 * n1 + n2 * n2 + n3 * n3);
1010
1011 normals[0] = n1 / norm;
1012 normals[1] = n2 / norm;
1013 normals[2] = n3 / norm;
1014 }
1015}
1016
1017} // namespace Nektar::FieldUtils
#define WARNINGL1(condition, msg)
Definition: ErrorUtil.hpp:243
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
A class that contains algorithms for interpolation between pts fields, expansions and different meshe...
FIELD_UTILS_EXPORT void Interpolate(const T expInField, T &expOutField, NekDouble def_value=0.0)
Interpolate from an expansion to an expansion.
FieldSharedPtr m_f
Field object.
Definition: Module.h:239
std::map< std::string, ConfigOption > m_config
List of configuration values.
Definition: Module.h:272
This processing module sets up for the boundary field to be extracted.
void v_Process(po::variables_map &vm) override
void ProjectPoint(const Array< OneD, const NekDouble > &gloCoord, const Array< OneD, const NekDouble > &projDir, const NekDouble distToOrig, Array< OneD, NekDouble > &projGloCoord)
Project a single point along the given direction to a plane.
bool BndElmtContainsPoint(SpatialDomains::GeometrySharedPtr bndGeom, const Array< OneD, const NekDouble > &gloCoord, const Array< OneD, const NekDouble > &projDir, Array< OneD, NekDouble > &locCoord, NekDouble &projDist, const NekDouble maxDist=1.0, const NekDouble iterTol=1.0e-8)
Check if a point can be projected onto an oundary element in a given direction. If yes,...
bool NewtonIterForLocCoordOnBndElmt(SpatialDomains::GeometrySharedPtr bndGeom, const Array< OneD, const NekDouble > &gloCoord, const Array< OneD, const Array< OneD, NekDouble > > &pts, const Array< OneD, const int > &dirUse, Array< OneD, NekDouble > &locCoord, NekDouble &dist, const NekDouble iterTol=1.0e-8, const int iterMax=51)
bool isInProjectedArea2D(const Array< OneD, const NekDouble > &projGloCoord, const Array< OneD, const Array< OneD, NekDouble > > &projPts, const NekDouble paralTol=1.0e-12)
Determine if the projected point is inside the projected element.
bool isInProjectedArea3D(const Array< OneD, const NekDouble > &projGloCoord, const Array< OneD, const Array< OneD, NekDouble > > &projPts, const Array< OneD, const NekDouble > &projDir, const NekDouble paralTol=1.0e-12, const NekDouble angleTol=1.0e-6)
bool BisectionForLocCoordOnBndElmt(SpatialDomains::GeometrySharedPtr bndGeom, const Array< OneD, const NekDouble > &gloCoord, const Array< OneD, const Array< OneD, NekDouble > > &pts, const Array< OneD, const int > &dirUse, Array< OneD, NekDouble > &locCoord, const NekDouble iterTol=1.0e-8, const int iterMax=51)
Use iteration to get the locCoord. This routine should be used after we have checked the projected po...
static std::shared_ptr< Module > create(FieldSharedPtr f)
Creates an instance of this class.
void GetNormals(SpatialDomains::GeometrySharedPtr bndGeom, const Array< OneD, const NekDouble > &locCoord, Array< OneD, NekDouble > &normals)
Get the normals for a given locCoord.
void v_Process(po::variables_map &vm) override
Write mesh to output file.
void ProjectVertices(const Array< OneD, const Array< OneD, NekDouble > > &pts, const Array< OneD, const NekDouble > &projDir, const NekDouble distToOrig, Array< OneD, Array< OneD, NekDouble > > &projPts)
Project a single point along the given direction to a plane.
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:197
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
static bool GenerateVector(const std::string &str, std::vector< T > &out)
Takes a comma-separated string and converts it to entries in a vector.
Definition: ParseUtils.cpp:130
const BoundaryRegionCollection & GetBoundaryRegions(void) const
Definition: Conditions.h:234
std::shared_ptr< Field > FieldSharedPtr
Definition: Field.hpp:990
std::pair< ModuleType, std::string > ModuleKey
Definition: Module.h:180
ModuleFactory & GetModuleFactory()
Definition: Module.cpp:47
static std::map< PtsInfo, int > NullPtsInfoMap
Definition: PtsField.h:66
static const NekDouble kNekUnsetDouble
std::map< int, BoundaryRegionShPtr > BoundaryRegionCollection
Definition: Conditions.h:210
std::shared_ptr< Geometry > GeometrySharedPtr
Definition: Geometry.h:51
std::shared_ptr< StdExpansion > StdExpansionSharedPtr
double NekDouble
void Neg(int n, T *x, const int incx)
Negate x = -x.
Definition: Vmath.hpp:292
T Vsum(int n, const T *x, const int incx)
Subtract return sum(x)
Definition: Vmath.hpp:608
T Dot(int n, const T *w, const T *x)
dot product
Definition: Vmath.hpp:761
void Vadd(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Add vector z = x+y.
Definition: Vmath.hpp:180
void Smul(int n, const T alpha, const T *x, const int incx, T *y, const int incy)
Scalar multiply y = alpha*x.
Definition: Vmath.hpp:100
scalarT< T > sqrt(scalarT< T > in)
Definition: scalar.hpp:294
Represents a command-line configuration option.
Definition: Module.h:129