Nektar++
ProcessBodyFittedVelocity.cpp
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////////////
2//
3// File: ProcessBodyFittedVelocity.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: Generate the body-fitted coordinate system and compute the
32// body-fitted velocity components.
33//
34////////////////////////////////////////////////////////////////////////////////
35
36#include <iostream>
37#include <string>
38
43
44#include "ProcessMapping.h"
46
49
50using namespace std;
51
52namespace Nektar::FieldUtils
53{
54
57 ModuleKey(eProcessModule, "bodyFittedVelocity"),
59 "Convert the velocity components from the Cartesian coordinate into "
60 "the body-fitted coordinate.");
61
64{
65 f->m_writeBndFld =
66 false; // turned on in the father class ProcessBoundaryExtract
67
68 // bnd is read from father class ProcessBoundaryExtract
69 m_config["assistDir"] =
70 ConfigOption(false, "0.0,0.0,1.0",
71 "The normal direction of the assistant plane, where \
72 the first body-fitted coordinate direction is located. \
73 The assistance plane is defined in point normal form. \
74 Default = [0,0,1]");
75 m_config["checkAngle"] =
76 ConfigOption(true, "0", "The flag for checking the distance vector \
77 perpendicular to the nearest element or not. It \
78 does not need to be turned on if geometry is \
79 smooth and there is no special consideration of \
80 the body-fitted coordinate system. It would be \
81 helpful to set up a desired coordinate system \
82 based on the surface with with sharp corners, \
83 such as at the presence of a step or gap. \
84 Default = false.");
85 m_config["distTol"] = ConfigOption(
86 false, "1.0e-12", "The distance tolerence to properly find out the \
87 nearest boundary element. It works together with \
88 the CHECKANGLE option to set up the body-fitted \
89 coordinate system near the corner. \
90 Default = 1.0e-12.");
91 m_config["bfcOut"] = ConfigOption(
92 true, "1", "The flag control whether the body-fitted coordinate \
93 system will be output. Default = true.");
94}
95
97{
98}
99
100/*Note
101 * This module converts the velocity components into the body-fitted coordinate.
102 * It works for both incompressible and compressible cases.
103 * The only requirement is the [u,v] fields the 2D case or [u,v,w] fields for
104 * 2.5D/3D case exist in the input fld fild.
105 * The input cases can be 2D, 2.5D and 3D.
106 * The data will be exported with .fld extension.
107 *
108 * Mapping is not supported at the moment
109 *
110 * The user defined parameters are: bnd, fixedDir
111 * - "bnd" for the wall reference id, which is defiend in the father class
112 * "ProcessBoundaryExtract".
113 * - "fixedDir" is the dorection at which the velocity component is not rotated.
114 */
115void ProcessBodyFittedVelocity::v_Process(po::variables_map &vm)
116{
118
119 // Get dim to store data
120 const int nFields = m_f->m_variables.size();
121 const int nCoordDim = m_f->m_exp[0]->GetCoordim(0);
122 const int nSpaceDim = nCoordDim + m_f->m_numHomogeneousDir;
123 const int nAddFields = nSpaceDim + 1; // distanceToWall, u_bfc, v_bfc, w_bfc
124
125 // Tolerence setting
126 // To include more elmts for further check
127 const NekDouble distTol = m_config["distTol"].as<NekDouble>();
128 const NekDouble geoTol = 1.0e-12; // To check if two pnts are too close
129 const NekDouble dirTol = 1.0e-4; // To check if two unit vecs are parallel
130 const NekDouble iterTol = 1.0e-12; // To check if locCoord iter convergence
131
132 const bool isCheckAngle = m_config["checkAngle"].as<bool>();
133 const bool isOutputBfc = m_config["bfcOut"].as<bool>();
134
135 // Get the assist vector
136 vector<NekDouble> assistDir;
137 ASSERTL0(ParseUtils::GenerateVector(m_config["assistDir"].as<string>(),
138 assistDir),
139 "Failed to interpret assistance direction");
140
141 Array<OneD, NekDouble> assistVec(3); // Save the assist vector
142 for (int i = 0; i < 3; ++i)
143 {
144 assistVec[i] = (assistDir.size() > i) ? assistDir[i] : 0.0;
145 }
146 if (nCoordDim == 2)
147 {
148 assistVec[0] = 0.0;
149 assistVec[1] = 0.0;
150 }
151
152 NekDouble norm = sqrt(Vmath::Dot(3, assistVec, 1, assistVec, 1));
153 if (norm < geoTol)
154 {
155 ASSERTL0(false, "Error. The amplitude of assist vector is smaller than \
156 the geometry tolerence 1.0e-12.");
157 }
158 Vmath::Smul(3, 1.0 / norm, assistVec, 1, assistVec, 1);
159
160 // Get the bnd id
161 // We only use the first one [0], check ProcessBoundaryExtract for more info
163 m_f->m_exp[0]->GetGraph());
165 bcs.GetBoundaryRegions();
166 map<int, int> BndRegionMap;
167 int cnt = 0;
168 for (auto &breg_it : bregions)
169 {
170 BndRegionMap[breg_it.first] = cnt++;
171 }
172 int bnd = BndRegionMap[m_f->m_bndRegionsToWrite[0]];
173
174 // Generate the distance array and three arrays for directional vectors
175 // bfcsDir[i][j][k]
176 // i - dir vec: 0-main tangential, 1-normal, (2-minor tangential)
177 // j - component: 0-x, 1-y, (2-z)
178 // k - point id
179 int npoints = m_f->m_exp[0]->GetNpoints();
180 Array<OneD, NekDouble> distance(npoints, 9999);
182 for (int i = 0; i < nSpaceDim; ++i)
183 {
184 bfcsDir[i] = Array<OneD, Array<OneD, NekDouble>>(nSpaceDim);
185
186 for (int j = 0; j < nSpaceDim; ++j)
187 {
188 bfcsDir[i][j] = Array<OneD, NekDouble>(npoints);
189 }
190 }
191
192 // Key function. At each quadrature point inside the domian, compute the
193 // body-fitted coordinate system with respect to the input bnd id
194 GenPntwiseBodyFittedCoordSys(bnd, assistVec, distance, bfcsDir,
195 isCheckAngle, distTol, iterTol, dirTol,
196 geoTol);
197
198 // Compute the velocity
199 Array<OneD, Array<OneD, NekDouble>> vel_car(nSpaceDim); // Cartesian
200 Array<OneD, Array<OneD, NekDouble>> vel_bfc(nSpaceDim); // body-fiitted
201 Array<OneD, NekDouble> vel_tmp(npoints);
202
203 for (int i = 0; i < nSpaceDim; ++i)
204 {
205 vel_bfc[i] = Array<OneD, NekDouble>(npoints, 0.0);
206 }
207
208 // Get the velocity in Cartesian coordinate system
210
211 // Project the velocity into the body-fitted coordinate system
212 for (int i = 0; i < nSpaceDim; ++i) // loop for bfc velocity
213 {
214 for (int j = 0; j < nSpaceDim; ++j)
215 {
216 Vmath::Vmul(npoints, vel_car[j], 1, bfcsDir[i][j], 1, vel_tmp, 1);
217 Vmath::Vadd(npoints, vel_tmp, 1, vel_bfc[i], 1, vel_bfc[i], 1);
218 }
219 }
220
221 // Add var names
222 m_f->m_variables.push_back("distanceToWall");
223 m_f->m_variables.push_back("u_bfc");
224 m_f->m_variables.push_back("v_bfc");
225 if (nSpaceDim == 3)
226 {
227 m_f->m_variables.push_back("w_bfc");
228 }
229 else if (nSpaceDim == 1)
230 {
231 ASSERTL0(false, "Velocity in 1D case is already in the body fitted \
232 coordinate. The dimension should be 2 or 3.");
233 }
234
235 // Add the new fields (distance + u,v,w_bfc)
236 // copy distance
237 m_f->m_exp.resize(nFields + nAddFields);
238
239 m_f->m_exp[nFields] = m_f->AppendExpList(m_f->m_numHomogeneousDir);
240 Vmath::Vcopy(npoints, distance, 1, m_f->m_exp[nFields]->UpdatePhys(), 1);
241 m_f->m_exp[nFields]->FwdTransLocalElmt(distance,
242 m_f->m_exp[nFields]->UpdateCoeffs());
243
244 // copy vel_bfc
245 for (int i = 1; i < nAddFields; ++i)
246 {
247 m_f->m_exp[nFields + i] = m_f->AppendExpList(m_f->m_numHomogeneousDir);
248 Vmath::Vcopy(npoints, vel_bfc[i - 1], 1,
249 m_f->m_exp[nFields + i]->UpdatePhys(), 1);
250 m_f->m_exp[nFields + i]->FwdTransLocalElmt(
251 vel_bfc[i - 1], m_f->m_exp[nFields + i]->UpdateCoeffs());
252 }
253
254 // Output the the body-fitted coordinate into the file
255 // The filter will read the coordiante through file
256 // bfcsDir[i][j][k]
257 // i - dir vec: 0-main tangential, 1-normal, (2-minor tangential)
258 // j - component: 0-x, 1-y, (2-z)
259 // k - point id
260 if (isOutputBfc)
261 {
262 m_f->m_variables.push_back("bfc_dir0_x");
263 m_f->m_variables.push_back("bfc_dir0_y");
264 if (nSpaceDim == 3)
265 {
266 m_f->m_variables.push_back("bfc_dir0_z");
267 }
268 m_f->m_variables.push_back("bfc_dir1_x");
269 m_f->m_variables.push_back("bfc_dir1_y");
270 if (nSpaceDim == 3)
271 {
272 m_f->m_variables.push_back("bfc_dir1_z");
273
274 m_f->m_variables.push_back("bfc_dir2_x");
275 m_f->m_variables.push_back("bfc_dir2_y");
276 m_f->m_variables.push_back("bfc_dir2_z");
277 }
278
279 int nAddFields_coord = (nSpaceDim == 3) ? 9 : 4;
280 m_f->m_exp.resize(nFields + nAddFields + nAddFields_coord);
281
282 cnt = 0;
283 for (int i = 0; i < nSpaceDim; ++i)
284 {
285 for (int j = 0; j < nSpaceDim; ++j)
286 {
287
288 m_f->m_exp[nFields + nAddFields + cnt] =
289 m_f->AppendExpList(m_f->m_numHomogeneousDir);
290
292 npoints, bfcsDir[i][j], 1,
293 m_f->m_exp[nFields + nAddFields + cnt]->UpdatePhys(), 1);
294 m_f->m_exp[nFields + nAddFields + cnt]->FwdTransLocalElmt(
295 bfcsDir[i][j],
296 m_f->m_exp[nFields + nAddFields + cnt]->UpdateCoeffs());
297
298 ++cnt;
299 }
300 }
301 }
302}
303
304/**
305 * @brief Compute the point-to-point distance to find the estimated cloest
306 * element.
307 * @param pts Global coordinate of the quadrature points in the inner
308 * element.
309 * @param pId Id of the inner point of interest in the current loop.
310 * @param bndPts Global coordinate of the quadrature points in the boundary
311 * element.
312 */
314 const Array<OneD, Array<OneD, NekDouble>> &pts, const int pId,
315 const Array<OneD, Array<OneD, NekDouble>> &bndPts)
316{
317 const int nCoordDim = pts.size();
318
319 NekDouble dist = 9999, dist_tmp;
320
321 for (int i = 0; i < bndPts[0].size(); ++i)
322 {
323 // Compute distance
324 dist_tmp = 0.0;
325 for (int j = 0; j < nCoordDim; ++j)
326 {
327 dist_tmp +=
328 (bndPts[j][i] - pts[j][pId]) * (bndPts[j][i] - pts[j][pId]);
329 }
330 dist_tmp = sqrt(dist_tmp);
331
332 if (dist_tmp < dist)
333 {
334 dist = dist_tmp;
335 }
336 }
337
338 return dist;
339}
340
341/**
342 * @brief Compute the local coordinate for the nearest point on the given
343 * 2D boundary element to the input point.
344 * @param inGloCoord Global coordinate for the input point.
345 * @param bndGeom Geometry of the boundary element to search from.
346 * @param pts Global coordinate of the quadrature points in the boundary
347 * element.
348 * @param locCoord Local coordinate of the result.
349 * @param gloCoord Global coordinate of the result.
350 * @param dist Distance from the input point to the boundary element.
351 * @param iterTol Iteration tolerence.
352 * @param iterMax Max iteration steps.
353 */
355 const Array<OneD, const NekDouble> &inGloCoord,
357 const Array<OneD, Array<OneD, NekDouble>> &pts,
359 NekDouble &dist, const NekDouble iterTol, const int iterMax)
360{
361
362 // Initial settings
363 Array<OneD, NekDouble> etaLR(2); // range [-1,1]
364 etaLR[0] = -1.0; // left
365 etaLR[1] = 1.0; // right
366 NekDouble tmpLx, tmpLy, tmpRx, tmpRy; // tmp values for L/R
367 NekDouble distL2, distR2; // distance square
368
369 StdRegions::StdExpansionSharedPtr bndXmap = bndGeom->GetXmap();
370 locCoord[0] = -2.0;
371 int cnt = 0;
372 bool isConverge = false;
373 while (cnt < iterMax)
374 {
375 tmpLx = bndXmap->PhysEvaluate(etaLR, pts[0]);
376 tmpLy = bndXmap->PhysEvaluate(etaLR, pts[1]);
377 tmpRx = bndXmap->PhysEvaluate(etaLR + 1, pts[0]);
378 tmpRy = bndXmap->PhysEvaluate(etaLR + 1, pts[1]);
379
380 distL2 = (tmpLx - inGloCoord[0]) * (tmpLx - inGloCoord[0]) +
381 (tmpLy - inGloCoord[1]) * (tmpLy - inGloCoord[1]);
382 distR2 = (tmpRx - inGloCoord[0]) * (tmpRx - inGloCoord[0]) +
383 (tmpRy - inGloCoord[1]) * (tmpRy - inGloCoord[1]);
384
385 if (distL2 >= distR2)
386 {
387 etaLR[0] = 0.5 * (etaLR[0] + etaLR[1]);
388 }
389 else
390 {
391 etaLR[1] = 0.5 * (etaLR[0] + etaLR[1]);
392 }
393
394 if ((etaLR[1] - etaLR[0]) < iterTol)
395 {
396 locCoord[0] = 0.5 * (etaLR[0] + etaLR[1]);
397 gloCoord[0] = 0.5 * (tmpLx + tmpRx);
398 gloCoord[1] = 0.5 * (tmpLy + tmpRy);
399 dist = sqrt(0.5 * (distL2 + distR2));
400 isConverge = true;
401 break;
402 }
403
404 ++cnt;
405 }
406
407 // Warning if failed
408 if (cnt >= iterMax)
409 {
410 WARNINGL1(false, "Bisection iteration is not converged");
411 }
412
413 return isConverge;
414}
415
416/**
417 * @brief Compute the local coordinate for the nearest point on the given
418 * boundary element to the input point. The locCoord is the position to
419 * set up the body-fitted coordinate. This function works as a driver.
420 * @param inGloCoord Global coordinate for the input point.
421 * @param bndGeom Geometry of the boundary element to search from.
422 * @param locCoord Local coordinate of the result.
423 * @param gloCoord Global coordinate of the result.
424 * @param dist Distance from the input point to the boundary element.
425 * @param iterTol Iteration tolerence.
426 * @param iterMax Max iteration steps.
427 */
429 const Array<OneD, const NekDouble> &inGloCoord,
431 Array<OneD, NekDouble> &gloCoord, NekDouble &dist, const NekDouble iterTol,
432 const int iterMax)
433{
434 bool isConverge = false;
435 const int nCoordDim = m_f->m_exp[0]->GetCoordim(0); // =2 for 2.5D cases
436
437 StdRegions::StdExpansionSharedPtr bndXmap = bndGeom->GetXmap();
438 int nBndPts = bndXmap->GetTotPoints();
439
440 Array<OneD, Array<OneD, const NekDouble>> bndCoeffs(nCoordDim);
441 Array<OneD, Array<OneD, NekDouble>> bndPts(nCoordDim);
442 for (int i = 0; i < nCoordDim; ++i)
443 {
444 bndPts[i] = Array<OneD, NekDouble>(nBndPts);
445 bndCoeffs[i] = bndGeom->GetCoeffs(i);
446 bndXmap->BwdTrans(bndCoeffs[i], bndPts[i]);
447 }
448
449 // Compute the locCoord for the pnt on the bnd elmt
450 if (nCoordDim == 2)
451 {
452 // Bisection search
454 inGloCoord, bndGeom, bndPts, locCoord, gloCoord, dist, iterTol,
455 iterMax);
456 }
457 else
458 {
459 // Bi-bisection search
460 ASSERTL0(false, "Not available at the moment.");
461 }
462
463 return isConverge;
464}
465
466/**
467 * @brief Compute the normalized cross product for two 2D vectors.
468 * vec3 = vec1 x vec2
469 */
471 const Array<OneD, NekDouble> &vec1, const Array<OneD, NekDouble> &vec2,
473{
474 vec3[0] = vec1[1] * vec2[2] - vec1[2] * vec2[1];
475 vec3[1] = vec1[2] * vec2[0] - vec1[0] * vec2[2];
476 vec3[2] = vec1[0] * vec2[1] - vec1[1] * vec2[0];
477
478 NekDouble coef;
479 coef =
480 1.0 / sqrt(vec3[0] * vec3[0] + vec3[1] * vec3[1] + vec3[2] * vec3[2]);
481
482 vec3[0] = vec3[0] * coef;
483 vec3[1] = vec3[1] * coef;
484 vec3[2] = vec3[2] * coef;
485}
486
487/**
488 * @brief At each quadrature point inside the domian, compute the body-fitted
489 * coordinate system with respect to the input boundary id.
490 * @param targetBndId Target boundary id.
491 * @param assistVec Unit assistant vector, the cross product of inward-
492 * pointing wall normalId and wihch gives of the main
493 * tangential direction of the body-fitted system.
494 * @param bfcsDir Pointwise body-fitted coordinate system.
495 * @param isPerpendicularCondition Flag for using perpendicular check or not
496 * @param distTol Distance tolerence. Used to find the boundary elements
497 * for local coordinate iteration.
498 * @param iterTol Iteration tolerence. Used to check iteration convergence.
499 * @param dirTol Direction tolerencce. Used to check if the inner product
500 * of two unit vectors is cloes enough to 1.0.
501 * @param geoTol Geometry tolerence. Used as the relative tolerence for
502 * local coord and distance absolute tolerence.
503 */
505 const int targetBndId, const Array<OneD, NekDouble> assistVec,
506 Array<OneD, NekDouble> &distance,
508 const bool isCheckAngle, const NekDouble distTol, const NekDouble iterTol,
509 const NekDouble dirTol, const NekDouble geoTol)
510{
511
512 const int nFields = m_f->m_variables.size();
513 const int nCoordDim = m_f->m_exp[0]->GetCoordim(0);
514 const int nSpaceDim = nCoordDim + m_f->m_numHomogeneousDir;
515 const int nBndLcoordDim = nCoordDim - 1;
516
517 // Get expansion list for boundary
519 for (int i = 0; i < nFields; ++i)
520 {
521 BndExp[i] = m_f->m_exp[i]->UpdateBndCondExpansion(targetBndId);
522 }
523
524 const int nElmts = m_f->m_exp[0]->GetNumElmts(); // num of inner element
525 const int nBndElmts = BndExp[0]->GetNumElmts(); // num of bnd element
526
527 // Use the outward-pointing normal at quardrature pts on bnd elmt as dir ref
529 m_f->m_exp[0]->GetBoundaryNormals(targetBndId, normalQ);
530
531 // Vars in the loop
534 Array<OneD, Array<OneD, NekDouble>> bndCoeffs(nCoordDim);
535 Array<OneD, Array<OneD, NekDouble>> bndPts(nCoordDim);
536
537 int nPts, nBndPts;
538 NekDouble dist_tmp, dist_bak;
539 bool isConverge;
540 vector<int> bndElmtIds;
541 int bndElmtId, bndElmtId_bak;
542 int phys_offset, bnd_phys_offset;
543
544 Array<OneD, NekDouble> inGloCoord(3, 0.0); // inner pnt
545 Array<OneD, NekDouble> locCoord(nBndLcoordDim), locCoord_tmp(nBndLcoordDim);
546 Array<OneD, NekDouble> gloCoord(3, 0.0), gloCoord_tmp(3, 0.0);
547 Array<OneD, NekDouble> normal(3), normalRef(3);
548 Array<OneD, NekDouble> tangential1(3),
549 tangential2(3); // 1 - main, 2 - minor
550 Array<OneD, NekDouble> normalChk(3);
551 ProcessWallNormalData wnd(m_f); // wallNormalData object to use its routine
552
553 // backup for angle check
554 Array<OneD, NekDouble> locCoord_bak(nBndLcoordDim), gloCoord_bak(3, 0.0);
555
556 //-------------------------------------------------------------------------
557 // Loop all the quadrature points of the field, and find out the closest
558 // point on the bnd for each of them. The bnd element contains this bnd
559 // point is recorded.
560
561 // Loop inner element
562 for (int eId = 0; eId < nElmts; ++eId) // element id, nElmts
563 {
564 // Get inner points
565 nPts = m_f->m_exp[0]->GetExp(eId)->GetTotPoints();
567 for (int i = 0; i < nCoordDim; ++i)
568 {
569 pts[i] = Array<OneD, NekDouble>(nPts, 0.0);
570 }
571
572 if (nCoordDim == 2)
573 {
574 m_f->m_exp[0]->GetExp(eId)->GetCoords(pts[0], pts[1]);
575 }
576 else if (nCoordDim == 3)
577 {
578 m_f->m_exp[0]->GetExp(eId)->GetCoords(pts[0], pts[1], pts[2]);
579 }
580
581 // Get the offset for the current elmt
582 phys_offset = m_f->m_exp[0]->GetPhys_Offset(eId);
583
584 // Loop points in the inner element
585 for (int pId = 0; pId < nPts; ++pId)
586 {
587
588 // Compute the distance from the pnt (bnd elmt) to the inner pnt
589 // Step 1 - Estimate search
590 // - Find the closest quadrature pnt among all the bnd elmt;
591 // - The bnd elmt which contains this pnt is saved for Step 2.
592 // * If the quadtature pnt is on the edge or corner of the bnd
593 // elmt, there will be more than one bnd elmt which contains
594 // this point.
595
596 bndElmtIds.clear(); // clear the bnd elmt id vec for a new inner pnt
597
598 // Loop bnd elmt to find the elmt to build body-fitted coord sys
599 for (int beId = 0; beId < nBndElmts; ++beId)
600 {
601 // Get geomery and points
602 bndGeom = BndExp[0]->GetExp(beId)->GetGeom();
603 bndXmap = bndGeom->GetXmap();
604 nBndPts = bndXmap->GetTotPoints();
605
606 for (int i = 0; i < nCoordDim; ++i)
607 {
608 bndPts[i] = Array<OneD, NekDouble>(nBndPts);
609 bndCoeffs[i] = bndGeom->GetCoeffs(i); // 0/1/2 for x/y/z
610 bndXmap->BwdTrans(bndCoeffs[i], bndPts[i]);
611 }
612
613 // Compute the distance (estimate)
614 dist_tmp = PntToBndElmtPntDistance(pts, pId, bndPts);
615
616 if ((dist_tmp < (distance[phys_offset + pId] - geoTol)) &&
617 (dist_tmp < (distance[phys_offset + pId] - distTol)))
618 {
619 // Find a closer quadrature point from a different bnd elmt
620 // Update distance and bnd elmt id vector
621 distance[phys_offset + pId] = dist_tmp;
622 bndElmtIds.clear();
623 bndElmtIds.push_back(beId);
624 }
625 else if ((dist_tmp < (distance[phys_offset + pId] + geoTol)) ||
626 (dist_tmp < (distance[phys_offset + pId] + distTol)))
627 {
628 // The same quadrature point from a different bnd elmt is
629 // found for the other time.
630 // Keep the distance and add the bnd elmt id to the vector
631 bndElmtIds.push_back(beId);
632 }
633
634 } // end of bnd elmt loop
635
636 // Step 2 - Accurate search
637 // - Find the accurate distance and locCoord for the nearest pnt
638 // (to the inner point) on the bnd elmt
639 // - This process will be repeated on all the bnd elmts in the bnd
640 // elmt id vector.
641
642 // Generate the coordinate for the inner point
643 for (int i = 0; i < nCoordDim; ++i)
644 {
645 inGloCoord[i] = pts[i][pId];
646 }
647
648 // reset the dist to make sure the condition for if will be
649 // satisfied
650 distance[phys_offset + pId] = 9999;
651 bndElmtId = -1;
652 dist_bak = 9999;
653 bndElmtId_bak = -1;
654
655 // Compute the accurate distance and locCoord
656 for (int i = 0; i < bndElmtIds.size(); ++i)
657 {
658 // Iterate on each of the possible bnd elmt to get the locCoord
659 // and the smallest dist
660 bndGeom = BndExp[0]->GetExp(bndElmtIds[i])->GetGeom();
661
663 inGloCoord, bndGeom, locCoord_tmp, gloCoord_tmp, dist_tmp,
664 iterTol, 51);
665
666 if (!isConverge)
667 {
668 WARNINGL1(false, "Bisection iteration is not converged!!!");
669 }
670
671 // Perpendicular check (angle check)
672 // When activated (distance > geoTol)
673 // if the pnt-to-pnt vector is not parallel with the wall
674 // normal, this bnd will be skipped. It is helpful to avoid
675 // confusion when there are cornors
676 if (isCheckAngle && (dist_tmp > geoTol)) // skip pts on the bnd
677 {
678 // Generate the scaled vector
679 Vmath::Vcopy(nCoordDim, gloCoord_tmp, 1, normalChk, 1);
680 Vmath::Neg(nCoordDim, normalChk, 1);
681 Vmath::Vadd(nCoordDim, inGloCoord, 1, normalChk, 1,
682 normalChk, 1);
683 Vmath::Smul(nCoordDim,
684 1.0 / sqrt(Vmath::Dot(nCoordDim, normalChk, 1,
685 normalChk, 1)),
686 normalChk, 1, normalChk, 1);
687
688 // Get normal based on the locCoord_tmp
689 wnd.GetNormals(bndGeom, locCoord_tmp, normal);
690
691 // Reverse the direction to make it point into the field
692 bnd_phys_offset = BndExp[0]->GetPhys_Offset(bndElmtIds[i]);
693 for (int j = 0; j < nCoordDim; ++j)
694 {
695 normalRef[j] = normalQ[j][bnd_phys_offset];
696 }
697
698 if (Vmath::Dot(nCoordDim, normal, normalRef) > 0.0)
699 {
700 Vmath::Neg(nCoordDim, normal, 1);
701 }
702
703 // Check if the current bnd elmt convers this inner pnt
704 if (Vmath::Dot(nCoordDim, normalChk, 1, normal, 1) <
705 (1 - dirTol))
706 {
707 // If not covered, save the nearest result as backup
708 // Then skip
709 if (dist_tmp < dist_bak)
710 {
711 bndElmtId_bak = bndElmtIds[i];
712 dist_bak = dist_tmp;
713
714 for (int j = 0; j < nBndLcoordDim; ++j)
715 {
716 locCoord_bak[j] = locCoord_tmp[j];
717 }
718
719 for (int j = 0; j < 3; ++j)
720 {
721 gloCoord_bak[j] = gloCoord_tmp[j];
722 }
723 }
724
725 continue; // Skip current bnd elmt
726 }
727 }
728
729 // No violation occurs, update the current result
730 // This section must be executed at least once if there the
731 // angle check is turned off. It means the following
732 // (bndElmtId == -1) can only be caused by angle check.
733 if (dist_tmp < distance[phys_offset + pId])
734 {
735 bndElmtId = bndElmtIds[i];
736 distance[phys_offset + pId] = dist_tmp;
737
738 for (int j = 0; j < nBndLcoordDim; ++j)
739 {
740 locCoord[j] = locCoord_tmp[j];
741 }
742
743 for (int j = 0; j < 3; ++j)
744 {
745 gloCoord[j] = gloCoord_tmp[j];
746 }
747 }
748 }
749
750 // Check if the bnd elmt is found. If not, use nearest one.
751 if (bndElmtId == -1)
752 {
753 if (bndElmtIds.size() == 0)
754 {
755 ASSERTL0(false, "No boundary element is found to be the \
756 possible nearest element. Please check.");
757 }
758
759 distance[phys_offset + pId] = dist_bak;
760 bndElmtId = bndElmtId_bak;
761
762 for (int i = 0; i < nBndLcoordDim; ++i)
763 {
764 locCoord[i] = locCoord_bak[i];
765 }
766
767 for (int i = 0; i < 3; ++i)
768 {
769 gloCoord[i] = gloCoord_bak[i];
770 }
771
772 WARNINGL1(false,
773 "The boundary element is not found under given \
774 tolerence. Please check and reset the \
775 tolerence, or just turn off the angle check. \
776 If you would like to continue, the nearest \
777 boundary element is used.");
778 }
779
780 // Get the wall normal using the function in wallNormalData class
781 bndGeom = BndExp[0]->GetExp(bndElmtId)->GetGeom();
782 wnd.GetNormals(bndGeom, locCoord, normal);
783
784 // Get ref normal for outward-point dir
785 bnd_phys_offset = BndExp[0]->GetPhys_Offset(bndElmtId);
786 for (int i = 0; i < nCoordDim; ++i)
787 {
788 normalRef[i] = normalQ[i][bnd_phys_offset];
789 }
790
791 // Correct the normal direction to make sure it points inward
792 if (Vmath::Dot(nCoordDim, normal, normalRef) > 0.0)
793 {
794 Vmath::Neg(nCoordDim, normal, 1);
795 }
796
797 // The main tagential direction is defined to be overlapped with the
798 // intersection line of the tangantial plane and the assistant
799 // plane, whose normal is the input assistDir. Both plane is defined
800 // using the point normal form, where the point is the nearest point
801 // on the boundary (gloCoord), and the vectors are the normal
802 // (pointing out) and the assistant direction (assistDir).
803 // Therefore, the main tangantial direction can be obtained by the
804 // cross product of the two vectors, since the two vectors has the
805 // same starting point and the tangantial direction is perpendicular
806 // to both of them.
807 ScaledCrosssProduct(normal, assistVec, tangential1);
808
809 // Save the direction vectors
810 for (int i = 0; i < nSpaceDim; ++i)
811 {
812 bfcsDir[0][i][phys_offset + pId] = tangential1[i];
813 bfcsDir[1][i][phys_offset + pId] = normal[i];
814 }
815
816 if (nSpaceDim == 3)
817 {
818 ScaledCrosssProduct(tangential1, normal, tangential2);
819 for (int i = 0; i < 3; ++i)
820 {
821 bfcsDir[2][i][phys_offset + pId] = tangential2[i];
822 }
823 }
824
825 } // end of inner pts loop
826
827 } // end of inner elmt loop
828}
829
830/**
831 * @brief Get velocity and convert to Cartesian system, if it is still in
832 * transformed system. It is copied and modified from from
833 * ProcessGrad.cpp
834 */
837{
838 int spacedim = m_f->m_exp[0]->GetCoordim(0) + m_f->m_numHomogeneousDir;
839 int nfields = m_f->m_variables.size();
840 int npoints = m_f->m_exp[0]->GetNpoints();
841 int var_offset = 0;
842
843 // Check type of the fields and set variable offset
844 vector<string> vars = m_f->m_exp[0]->GetSession()->GetVariables();
845 if (boost::iequals(vars[0], "rho") && boost::iequals(vars[1], "rhou"))
846 {
847 var_offset = spacedim + 2;
848 }
849
850 // Get mapping
852
853 // Get velocity and convert to Cartesian system,
854 // if it is still in transformed system
855 if (m_f->m_fieldMetaDataMap.count("MappingCartesianVel"))
856 {
857 if (m_f->m_fieldMetaDataMap["MappingCartesianVel"] == "False")
858 {
859 // Initialize arrays and copy velocity
860 for (int i = 0; i < spacedim; ++i)
861 {
862 vel[i] = Array<OneD, NekDouble>(npoints);
863 if (m_f->m_exp[0]->GetWaveSpace())
864 {
865 m_f->m_exp[0]->HomogeneousBwdTrans(
866 npoints, m_f->m_exp[var_offset + i]->GetPhys(), vel[i]);
867 }
868 else
869 {
870 Vmath::Vcopy(npoints, m_f->m_exp[var_offset + i]->GetPhys(),
871 1, vel[i], 1);
872 }
873 }
874 // Convert velocity to cartesian system
875 mapping->ContravarToCartesian(vel, vel);
876 // Convert back to wavespace if necessary
877 if (m_f->m_exp[0]->GetWaveSpace())
878 {
879 for (int i = 0; i < spacedim; ++i)
880 {
881 m_f->m_exp[0]->HomogeneousFwdTrans(npoints, vel[i], vel[i]);
882 }
883 }
884 }
885 else
886 {
887 for (int i = 0; i < spacedim; ++i)
888 {
889 vel[i] = Array<OneD, NekDouble>(npoints);
890 Vmath::Vcopy(npoints, m_f->m_exp[var_offset + i]->GetPhys(), 1,
891 vel[i], 1);
892 }
893 }
894 }
895 else
896 {
897 for (int i = 0; i < spacedim && i < nfields; ++i)
898 {
899 vel[i] = Array<OneD, NekDouble>(npoints);
900 Vmath::Vcopy(npoints, m_f->m_exp[var_offset + i]->GetPhys(), 1,
901 vel[i], 1);
902 }
903 }
904}
905
906} // namespace Nektar::FieldUtils
#define WARNINGL1(condition, msg)
Definition: ErrorUtil.hpp:243
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
FieldSharedPtr m_f
Field object.
Definition: Module.h:239
std::map< std::string, ConfigOption > m_config
List of configuration values.
Definition: Module.h:272
void v_Process(po::variables_map &vm) override
Write mesh to output file.
void GenPntwiseBodyFittedCoordSys(const int targetBndId, const Array< OneD, NekDouble > assistVec, Array< OneD, NekDouble > &distance, Array< OneD, Array< OneD, Array< OneD, NekDouble > > > &bfcsDir, const bool isCheckAngle, const NekDouble distTol=1.0e-12, const NekDouble iterTol=1.0e-12, const NekDouble dirTol=1.0e-4, const NekDouble geoTol=1.0e-12)
At each quadrature point inside the domian, compute the body-fitted coordinate system with respect to...
void GetVelAndConvertToCartSys(Array< OneD, Array< OneD, NekDouble > > &vel)
Get velocity and convert to Cartesian system, if it is still in transformed system....
void ScaledCrosssProduct(const Array< OneD, NekDouble > &vec1, const Array< OneD, NekDouble > &vec2, Array< OneD, NekDouble > &vec3)
Compute the normalized cross product for two 2D vectors. vec3 = vec1 x vec2.
bool LocCoordForNearestPntOnBndElmt_2D(const Array< OneD, const NekDouble > &inGloCoord, SpatialDomains::GeometrySharedPtr bndGeom, const Array< OneD, Array< OneD, NekDouble > > &pts, Array< OneD, NekDouble > &locCoord, Array< OneD, NekDouble > &gloCoord, NekDouble &dist, const NekDouble iterTol=1.0e-12, const int iterMax=51)
Compute the local coordinate for the nearest point on the given 2D boundary element to the input poin...
static std::shared_ptr< Module > create(FieldSharedPtr f)
Creates an instance of this class.
NekDouble PntToBndElmtPntDistance(const Array< OneD, Array< OneD, NekDouble > > &pts, const int pId, const Array< OneD, Array< OneD, NekDouble > > &bndPts)
Compute the local coordinate for the nearest point on the given 2D boundary element to the input poin...
bool LocCoordForNearestPntOnBndElmt(const Array< OneD, const NekDouble > &inGloCoord, SpatialDomains::GeometrySharedPtr bndGeom, Array< OneD, NekDouble > &locCoord, Array< OneD, NekDouble > &gloCoord, NekDouble &dist, const NekDouble iterTol=1.0e-12, const int iterMax=51)
Compute the local coordinate for the nearest point on the given boundary element to the input point....
This processing module sets up for the boundary field to be extracted.
void v_Process(po::variables_map &vm) override
static GlobalMapping::MappingSharedPtr GetMapping(FieldSharedPtr f)
This processing module calculates the wall shear stress and adds it as an extra-field to the output f...
void GetNormals(SpatialDomains::GeometrySharedPtr bndGeom, const Array< OneD, const NekDouble > &locCoord, Array< OneD, NekDouble > &normals)
Get the normals for a given locCoord.
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:197
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
GLOBAL_MAPPING_EXPORT typedef std::shared_ptr< Mapping > MappingSharedPtr
A shared pointer to a Mapping object.
Definition: Mapping.h:51
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 Vmul(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Multiply vector z = x*y.
Definition: Vmath.hpp:72
void Neg(int n, T *x, const int incx)
Negate x = -x.
Definition: Vmath.hpp:292
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
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.hpp:825
scalarT< T > sqrt(scalarT< T > in)
Definition: scalar.hpp:294
Represents a command-line configuration option.
Definition: Module.h:129