Nektar++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ProcessBL.cpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: ProcessBL.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 // License for the specific language governing rights and limitations under
14 // Permission is hereby granted, free of charge, to any person obtaining a
15 // copy of this software and associated documentation files (the "Software"),
16 // to deal in the Software without restriction, including without limitation
17 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 // and/or sell copies of the Software, and to permit persons to whom the
19 // Software is furnished to do so, subject to the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be included
22 // in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 // DEALINGS IN THE SOFTWARE.
31 //
32 // Description: Refine prismatic boundary layer elements.
33 //
34 ////////////////////////////////////////////////////////////////////////////////
35 
36 #include <string>
37 using namespace std;
38 
39 #include "MeshElements.h"
40 #include "ProcessBL.h"
41 
47 #include <LocalRegions/PrismExp.h>
48 
49 namespace Nektar
50 {
51  namespace Utilities
52  {
53  ModuleKey ProcessBL::className =
55  ModuleKey(eProcessModule, "bl"), ProcessBL::create,
56  "Refines a prismatic boundary layer.");
57 
58  ProcessBL::ProcessBL(MeshSharedPtr m) : ProcessModule(m)
59  {
60  // BL mesh configuration.
61  m_config["layers"] = ConfigOption(false, "2",
62  "Number of layers to refine.");
63  m_config["nq"] = ConfigOption(false, "5",
64  "Number of points in high order elements.");
65  m_config["surf"] = ConfigOption(false, "",
66  "Tag identifying surface connected to prism.");
67  m_config["r"] = ConfigOption(false, "2.0",
68  "Ratio to use in geometry progression.");
69  }
70 
72  {
73 
74  }
75 
77  {
78  if (m_mesh->m_verbose)
79  {
80  cout << "ProcessBL: Refining prismatic boundary layer..."
81  << endl;
82  }
83 
84  int nodeId = m_mesh->m_vertexSet.size();
85  int nl = m_config["layers"].as<int>();
86  int nq = m_config["nq"]. as<int>();
87 
88  // determine if geometric ratio is string or a constant.
90  NekDouble r;
91  int rExprId = -1;
92  bool ratioIsString = false;
93 
94  if (m_config["r"].isType<NekDouble>())
95  {
96  r = m_config["r"].as<NekDouble>();
97  }
98  else
99  {
100  std::string rstr = m_config["r"].as<string>();
101  rExprId = rEval.DefineFunction("x y z", rstr);
102  ratioIsString = true;
103  }
104 
105  // Prismatic node -> face map.
106  int prismFaceNodes[5][4] = {
107  {0,1,2,3},{0,1,4,-1},{1,2,5,4},{3,2,5,-1},{0,3,5,4}};
108 
109  // Default PointsType.
111 
112  // Map which takes element ID to face on surface. This enables
113  // splitting to occur in either y-direction of the prism.
114  map<int, int> splitEls;
115 
116  // Set up maps which takes an edge (in nektar++ ordering) and return
117  // their offset and stride in the 3d array of collapsed quadrature
118  // points. Note that this map includes only the edges that are on
119  // the triangular faces as the edges in the normal direction are
120  // linear.
121  int splitMapEdge[6] = {0,2,4,5,6,7};
122  int splitMapOffset[6][2] = {
123  {0, 1 },
124  {nq, 1 },
125  {0, nq*(nl+1)},
126  {nq-1, nq*(nl+1)},
127  {nq+nq-1, nq*(nl+1)},
128  {nq, nq*(nl+1)}
129  };
130 
131  // splitEdge enumerates the edges in the standard prism along which
132  // new nodes should be generated. These edges are the three between
133  // the two triangular faces.
134  int splitEdge[3] = {3,1,8};
135 
136  // edgeVertMap specifies the vertices which comprise those edges in
137  // splitEdge; for example splitEdge[0] = 3 which connects vertices 0
138  // and 3.
139  int edgeVertMap[3][2] = {{0,3}, {1,2}, {4,5}};
140 
141  // edgeOffset holds the offset of each of edges 3, 1 and 8
142  // respectively inside the collapsed coordinate system.
143  int edgeOffset[3] = {0, nq-1, nq*(nl+1)*(nq-1)};
144 
145  // edgeMap associates geometry edge IDs to the (nl+1) vertices which
146  // are generated along that edge when a prism is split, and is used
147  // to avoid generation of duplicate vertices. It is stored as an
148  // unordered map for speed.
149  boost::unordered_map<int, vector<NodeSharedPtr> > edgeMap;
150  boost::unordered_map<int, vector<NodeSharedPtr> >::iterator eIt;
151 
152  string surf = m_config["surf"].as<string>();
153  if (surf.size() > 0)
154  {
155  vector<unsigned int> surfs;
156  ParseUtils::GenerateSeqVector(surf.c_str(), surfs);
157  sort(surfs.begin(), surfs.end());
158 
159  // If surface is defined, process list of elements to find those
160  // that are connected to it.
161  for (int i = 0; i < m_mesh->m_element[m_mesh->m_expDim].size(); ++i)
162  {
163  ElementSharedPtr el = m_mesh->m_element[m_mesh->m_expDim][i];
164  int nSurf = el->GetFaceCount();
165 
166  for (int j = 0; j < nSurf; ++j)
167  {
168  int bl = el->GetBoundaryLink(j);
169  if (bl == -1)
170  {
171  continue;
172  }
173 
174  ElementSharedPtr bEl = m_mesh->m_element[m_mesh->m_expDim-1][bl];
175  vector<int> tags = bEl->GetTagList();
176  vector<int> inter;
177 
178  sort(tags.begin(), tags.end());
179  set_intersection(surfs.begin(), surfs.end(),
180  tags .begin(), tags .end(),
181  back_inserter(inter));
182  ASSERTL0(inter.size() <= 1,
183  "Intersection of surfaces wrong");
184 
185  if (inter.size() == 1)
186  {
187  if (el->GetConf().m_e != LibUtilities::ePrism)
188  {
189  cerr << "WARNING: Found non-prismatic element "
190  << "to split in surface " << surf
191  << "; ignoring" << endl;
192  continue;
193  }
194 
195  if (j % 2 == 0)
196  {
197  cerr << "WARNING: Found quadrilateral face "
198  << j << " on surface " << surf
199  << " connected to prism; ignoring."
200  << endl;
201  continue;
202  }
203 
204  if (splitEls.count(el->GetId()) > 0)
205  {
206  cerr << "WARNING: prism already found; "
207  << "ignoring" << endl;
208  }
209 
210  splitEls[el->GetId()] = j;
211  }
212  }
213  }
214  }
215  else
216  {
217  // Otherwise, add all prismatic elements and assume face 1 of
218  // the prism lies on the surface.
219  for (int i = 0; i < m_mesh->m_element[m_mesh->m_expDim].size(); ++i)
220  {
221  ElementSharedPtr el = m_mesh->m_element[m_mesh->m_expDim][i];
222 
223  if (el->GetConf().m_e != LibUtilities::ePrism)
224  {
225  continue;
226  }
227 
228  splitEls[el->GetId()] = 1;
229  }
230  }
231 
232  if (splitEls.size() == 0)
233  {
234  cerr << "WARNING: No elements detected to split." << endl;
235  return;
236  }
237 
238  // Erase all elements from the element list. Elements will be
239  // re-added as they are split.
240  vector<ElementSharedPtr> el = m_mesh->m_element[m_mesh->m_expDim];
241  m_mesh->m_element[m_mesh->m_expDim].clear();
242 
243  // Iterate over list of elements of expansion dimension.
244  for (int i = 0; i < el.size(); ++i)
245  {
246  if (splitEls.count(el[i]->GetId()) == 0)
247  {
248  m_mesh->m_element[m_mesh->m_expDim].push_back(el[i]);
249  continue;
250  }
251 
252  // Find quadrilateral boundary faces if any
253  std::map<int, int> bLink;
254  for (int j = 0; j < 5; j += 2)
255  {
256  int bl = el[i]->GetBoundaryLink(j);
257  if (bl != -1)
258  {
259  bLink[j] = bl;
260  }
261  }
262 
263  // Get elemental geometry object.
265  boost::dynamic_pointer_cast<SpatialDomains::PrismGeom>(
266  el[i]->GetGeom(m_mesh->m_spaceDim));
267 
268  // Determine whether to use reverse points.
269  LibUtilities::PointsType t = splitEls[el[i]->GetId()] == 1 ?
272 
273  if(ratioIsString) // determien value of r base on geom
274  {
275  NekDouble x,y,z;
276  NekDouble x1,y1,z1;
277  int nverts = geom->GetNumVerts();
278 
279  x = y = z = 0.0;
280 
281  for(int i = 0; i < nverts; ++i)
282  {
283  geom->GetVertex(i)->GetCoords(x1,y1,z1);
284  x += x1; y += y1; z += z1;
285  }
286  x /= (NekDouble) nverts;
287  y /= (NekDouble) nverts;
288  z /= (NekDouble) nverts;
289  r = rEval.Evaluate(rExprId,x,y,z,0.0);
290  }
291 
292 
293  // Create basis.
296  LibUtilities::PointsKey(nq,pt));
299  LibUtilities::PointsKey(nl+1, t, r));
302  LibUtilities::PointsKey(nq,pt));
303 
304  // Create local region.
307  B0,B1,B2,geom);
308 
309  // Grab co-ordinates.
310  Array<OneD, NekDouble> x(nq*nq*(nl+1));
311  Array<OneD, NekDouble> y(nq*nq*(nl+1));
312  Array<OneD, NekDouble> z(nq*nq*(nl+1));
313  q->GetCoords(x,y,z);
314 
315  vector<vector<NodeSharedPtr> > edgeNodes(3);
316 
317  // Loop over edges to be split.
318  for (int j = 0; j < 3; ++j)
319  {
320  int locEdge = splitEdge[j];
321  int edgeId = el[i]->GetEdge(locEdge)->m_id;
322 
323  // Determine whether we have already generated vertices
324  // along this edge.
325  eIt = edgeMap.find(edgeId);
326 
327  if (eIt == edgeMap.end())
328  {
329  // If not then resize storage to hold new points.
330  edgeNodes[j].resize(nl+1);
331 
332  // Re-use existing vertices at endpoints of edge to
333  // avoid duplicating the existing vertices.
334  edgeNodes[j][0] = el[i]->GetVertex(edgeVertMap[j][0]);
335  edgeNodes[j][nl] = el[i]->GetVertex(edgeVertMap[j][1]);
336 
337  // Variable geometric ratio
338  if(ratioIsString)
339  {
340  NekDouble x0,y0,z0;
341  NekDouble x1,y1,z1;
342  NekDouble xm,ym,zm;
343 
344  // -> Find edge end and mid points
345  x0 = x[edgeOffset[j]];
346  y0 = y[edgeOffset[j]];
347  z0 = z[edgeOffset[j]];
348 
349  x1 = x[edgeOffset[j]+nl*nq];
350  y1 = y[edgeOffset[j]+nl*nq];
351  z1 = z[edgeOffset[j]+nl*nq];
352 
353  xm = 0.5*(x0+x1);
354  ym = 0.5*(y0+y1);
355  zm = 0.5*(z0+z1);
356 
357  // evaluate r factor based on mid point value
358  NekDouble rnew;
359  rnew = rEval.Evaluate(rExprId,xm,ym,zm,0.0);
360 
361  // Get basis with new r;
362  LibUtilities::PointsKey Pkey(nl+1, t, rnew);
364  = LibUtilities::PointsManager()[Pkey];
365 
366  const Array<OneD, const NekDouble> z = newP->GetZ();
367 
368  // Create new interior nodes based on this new blend
369  for (int k = 1; k < nl; ++k)
370  {
371  xm = 0.5*(1+z[k])*(x1-x0) + x0;
372  ym = 0.5*(1+z[k])*(y1-y0) + y0;
373  zm = 0.5*(1+z[k])*(z1-z0) + z0;
374  edgeNodes[j][k] = NodeSharedPtr(
375  new Node(nodeId++, xm,ym,zm));
376  }
377  }
378  else
379  {
380  // Create new interior nodes.
381  for (int k = 1; k < nl; ++k)
382  {
383  int pos = edgeOffset[j] + k*nq;
384  edgeNodes[j][k] = NodeSharedPtr(
385  new Node(nodeId++, x[pos], y[pos], z[pos]));
386  }
387  }
388 
389  // Store these edges in edgeMap.
390  edgeMap[edgeId] = edgeNodes[j];
391  }
392  else
393  {
394  edgeNodes[j] = eIt->second;
395  }
396  }
397 
398  // Create element layers.
399  for (int j = 0; j < nl; ++j)
400  {
401  // Offset of this layer within the collapsed coordinate
402  // system.
403  int offset = j*nq;
404 
405  // Get corner vertices.
406  vector<NodeSharedPtr> nodeList(6);
407  nodeList[0] = edgeNodes[0][j ];
408  nodeList[1] = edgeNodes[1][j ];
409  nodeList[2] = edgeNodes[1][j+1];
410  nodeList[3] = edgeNodes[0][j+1];
411  nodeList[4] = edgeNodes[2][j ];
412  nodeList[5] = edgeNodes[2][j+1];
413 
414  // Create the element.
415  ElmtConfig conf(LibUtilities::ePrism, 1, true, true, false);
417  CreateInstance(
418  LibUtilities::ePrism,conf,nodeList,el[i]->GetTagList());
419 
420  // Add high order nodes to split prismatic edges.
421  for (int l = 0; l < 6; ++l)
422  {
423  EdgeSharedPtr HOedge = elmt->GetEdge(
424  splitMapEdge[l]);
425  for (int k = 1; k < nq-1; ++k)
426  {
427  int pos = offset + splitMapOffset[l][0] +
428  k*splitMapOffset[l][1];
429  HOedge->m_edgeNodes.push_back(
431  new Node(nodeId++,x[pos],y[pos],z[pos])));
432  }
433  HOedge->m_curveType = pt;
434  }
435 
436  // Change the surface elements of the quad face on the
437  // symmetry plane to match the layers of prisms.
439  for (it = bLink.begin(); it != bLink.end(); ++it)
440  {
441  int fid = it->first;
442  int bl = it->second;
443 
444  if (j == 0)
445  {
446  // For first layer reuse existing 2D element.
447  ElementSharedPtr e = m_mesh->m_element[m_mesh->m_expDim-1][bl];
448  for (int k = 0; k < 4; ++k)
449  {
450  e->SetVertex(
451  k, nodeList[prismFaceNodes[fid][k]]);
452  }
453  }
454  else
455  {
456  // For all other layers create new element.
457  vector<NodeSharedPtr> qNodeList(4);
458  for (int k = 0; k < 4; ++k)
459  {
460  qNodeList[k] = nodeList[prismFaceNodes[fid][k]];
461  }
462  vector<int> tagBE;
463  tagBE = m_mesh->m_element[m_mesh->m_expDim-1][bl]->GetTagList();
464  ElmtConfig bconf(LibUtilities::eQuadrilateral,1,true,true,false);
465  ElementSharedPtr boundaryElmt = GetElementFactory().
466  CreateInstance(LibUtilities::eQuadrilateral,bconf,
467  qNodeList,tagBE);
468  m_mesh->m_element[m_mesh->m_expDim-1].push_back(boundaryElmt);
469  }
470  }
471 
472  m_mesh->m_element[m_mesh->m_expDim].push_back(elmt);
473  }
474  }
475 
476  // Re-process mesh to eliminate duplicate vertices and edges.
477  ProcessVertices();
478  ProcessEdges();
479  ProcessFaces();
480  ProcessElements();
482  }
483  }
484 }