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