Nektar++
Public Member Functions | Static Public Member Functions | Static Public Attributes | Protected Member Functions | List of all members
Nektar::Utilities::ProcessSpherigon Class Reference

#include <ProcessSpherigon.h>

Inheritance diagram for Nektar::Utilities::ProcessSpherigon:
Inheritance graph
[legend]
Collaboration diagram for Nektar::Utilities::ProcessSpherigon:
Collaboration graph
[legend]

Public Member Functions

 ProcessSpherigon (MeshSharedPtr m)
 Default constructor. More...
 
virtual ~ProcessSpherigon ()
 Destructor. More...
 
virtual void Process ()
 Write mesh to output file. More...
 
- Public Member Functions inherited from Nektar::Utilities::ProcessModule
 ProcessModule ()
 
 ProcessModule (FieldSharedPtr p_f)
 
 ProcessModule (MeshSharedPtr p_m)
 
- Public Member Functions inherited from Nektar::Utilities::Module
 Module (FieldSharedPtr p_f)
 
virtual void Process (po::variables_map &vm)=0
 
void RegisterConfig (string key, string value)
 Register a configuration option with a module. More...
 
void PrintConfig ()
 Print out all configuration options for a module. More...
 
void SetDefaults ()
 Sets default configuration options for those which have not been set. More...
 
bool GetRequireEquiSpaced (void)
 
void SetRequireEquiSpaced (bool pVal)
 
void EvaluateTriFieldAtEquiSpacedPts (LocalRegions::ExpansionSharedPtr &exp, const Array< OneD, const NekDouble > &infield, Array< OneD, NekDouble > &outfield)
 
 Module (MeshSharedPtr p_m)
 
void RegisterConfig (string key, string value)
 
void PrintConfig ()
 
void SetDefaults ()
 
MeshSharedPtr GetMesh ()
 
virtual void ProcessVertices ()
 Extract element vertices. More...
 

Static Public Member Functions

static boost::shared_ptr< Modulecreate (MeshSharedPtr m)
 Creates an instance of this class. More...
 

Static Public Attributes

static ModuleKey className
 

Protected Member Functions

void GenerateNormals (vector< ElementSharedPtr > &el, MeshSharedPtr &mesh)
 Generate a set of approximate vertex normals to a surface represented by line segments in 2D and a hybrid triangular/quadrilateral mesh in 3D. More...
 
double CrossProdMag (Node &a, Node &b)
 Calculate the magnitude of the cross product $ \vec{a}\times\vec{b} $. More...
 
void UnitCrossProd (Node &a, Node &b, Node &c)
 
double Blend (double r)
 Calculate the $ C^0 $ blending function for spherigon implementation. More...
 
void SuperBlend (vector< double > &r, vector< Node > &Q, Node &P, vector< double > &blend)
 Calculate the $ C^1 $ blending function for spherigon implementation. More...
 
- Protected Member Functions inherited from Nektar::Utilities::Module
 Module ()
 
virtual void ProcessEdges (bool ReprocessEdges=true)
 Extract element edges. More...
 
virtual void ProcessFaces (bool ReprocessFaces=true)
 Extract element faces. More...
 
virtual void ProcessElements ()
 Generate element IDs. More...
 
virtual void ProcessComposites ()
 Generate composites. More...
 
void ReorderPrisms (PerMap &perFaces)
 Reorder node IDs so that prisms and tetrahedra are aligned correctly. More...
 
void PrismLines (int prism, PerMap &perFaces, set< int > &prismsDone, vector< ElementSharedPtr > &line)
 

Additional Inherited Members

- Protected Attributes inherited from Nektar::Utilities::Module
FieldSharedPtr m_f
 Field object. More...
 
map< string, ConfigOptionm_config
 List of configuration values. More...
 
bool m_requireEquiSpaced
 
MeshSharedPtr m_mesh
 Mesh object. More...
 

Detailed Description

This class implements the spherigon surface smoothing technique which is documented in

"The SPHERIGON: A Simple Polygon Patch for Smoothing Quickly your Polygonal Meshes": P. Volino and N. Magnenat Thalmann, Computer Animation Proceedings (1998).

This implementation works in both a 2D manifold setting (for triangles and quadrilaterals embedded in 3-space) and in a full 3D enviroment (prisms, tetrahedra and hexahedra).

No additional information needs to be supplied directly to the module in order for it to work. However, 3D elements do rely on the mapping Mesh::spherigonSurfs to be populated by the relevant input modules.

Additionally, since the algorithm assumes normals are supplied which are perpendicular to the true surface defined at each vertex. If these are specified in Mesh::vertexNormals by the input module, better smoothing results can be obtained. Otherwise, normals are estimated by taking the average of all edge/face normals which connect to the vertex.

Definition at line 47 of file ProcessSpherigon.h.

Constructor & Destructor Documentation

Nektar::Utilities::ProcessSpherigon::ProcessSpherigon ( MeshSharedPtr  m)

Default constructor.

Definition at line 90 of file ProcessSpherigon.cpp.

References Nektar::Utilities::Module::m_config.

90  : ProcessModule(m)
91  {
92  m_config["N"] = ConfigOption(false, "5",
93  "Number of points to add to face edges.");
94  m_config["surf"] = ConfigOption(false, "-1",
95  "Tag identifying surface to process.");
96  m_config["BothTriFacesOnPrism"] = ConfigOption(true, "-1",
97  "Curve both triangular faces of prism on boundary.");
98  m_config["usenormalfile"] = ConfigOption(false,"NoFile",
99  "Use alternative file for Spherigon definition");
100  m_config["scalefile"] = ConfigOption(false,"1.0",
101  "Apply scaling factor to coordinates in file ");
102  m_config["normalnoise"] = ConfigOption(false,"NotSpecified",
103  "Add randowm noise to normals of amplitude AMP in specified region. input string is Amp,xmin,xmax,ymin,ymax,zmin,zmax");
104  }
map< string, ConfigOption > m_config
List of configuration values.
Nektar::Utilities::ProcessSpherigon::~ProcessSpherigon ( )
virtual

Destructor.

Definition at line 109 of file ProcessSpherigon.cpp.

110  {
111 
112  }

Member Function Documentation

double Nektar::Utilities::ProcessSpherigon::Blend ( double  r)
protected

Calculate the $ C^0 $ blending function for spherigon implementation.

See equation (5) of the paper.

Parameters
rBarycentric coordinate.

Definition at line 153 of file ProcessSpherigon.cpp.

154  {
155  return r*r;
156  }
static boost::shared_ptr<Module> Nektar::Utilities::ProcessSpherigon::create ( MeshSharedPtr  m)
inlinestatic

Creates an instance of this class.

Definition at line 51 of file ProcessSpherigon.h.

References Nektar::MemoryManager< DataType >::AllocateSharedPtr().

52  {
54  }
static boost::shared_ptr< DataType > AllocateSharedPtr()
Allocate a shared pointer from the memory pool.
double Nektar::Utilities::ProcessSpherigon::CrossProdMag ( Node a,
Node b 
)
protected

Calculate the magnitude of the cross product $ \vec{a}\times\vec{b} $.

Definition at line 137 of file ProcessSpherigon.cpp.

References Nektar::Utilities::Node::m_x, Nektar::Utilities::Node::m_y, and Nektar::Utilities::Node::m_z.

Referenced by Process().

138  {
139  double tmp1 = a.m_y*b.m_z - a.m_z*b.m_y;
140  double tmp2 = a.m_z*b.m_x - a.m_x*b.m_z;
141  double tmp3 = a.m_x*b.m_y - a.m_y*b.m_x;
142  return sqrt(tmp1*tmp1 + tmp2*tmp2 + tmp3*tmp3);
143  }
void Nektar::Utilities::ProcessSpherigon::GenerateNormals ( std::vector< ElementSharedPtr > &  el,
MeshSharedPtr mesh 
)
protected

Generate a set of approximate vertex normals to a surface represented by line segments in 2D and a hybrid triangular/quadrilateral mesh in 3D.

This routine approximates the true vertex normals to a surface by averaging the normals of all edges/faces which connect to the vertex. It is better to use the exact surface normals which can be set in Mesh::vertexNormals, but where they are not supplied this routine calculates an approximation for the spherigon implementation.

Parameters
elVector of elements denoting the surface mesh.

Definition at line 223 of file ProcessSpherigon.cpp.

References Nektar::Utilities::Node::abs2(), ASSERTL0, Nektar::LibUtilities::eQuadrilateral, Nektar::LibUtilities::eSegment, Nektar::LibUtilities::eTriangle, Nektar::iterator, Nektar::Utilities::Node::m_x, Nektar::Utilities::Node::m_y, Nektar::Utilities::Node::m_z, and UnitCrossProd().

Referenced by Process().

225  {
227 
228 
229 
230  for (int i = 0; i < el.size(); ++i)
231  {
232  ElementSharedPtr e = el[i];
233 
234  // Ensure that element is a line, triangle or quad.
235  ASSERTL0(e->GetConf().m_e == LibUtilities::eSegment ||
236  e->GetConf().m_e == LibUtilities::eTriangle ||
237  e->GetConf().m_e == LibUtilities::eQuadrilateral,
238  "Spherigon expansions must be lines, triangles or "
239  "quadrilaterals.");
240 
241  // Calculate normal for this element.
242  int nV = e->GetVertexCount();
243  vector<NodeSharedPtr> node(nV);
244 
245  for (int j = 0; j < nV; ++j)
246  {
247  node[j] = e->GetVertex(j);
248  }
249 
250  Node n;
251 
252  if (mesh->m_spaceDim == 3)
253  {
254  // Create two tangent vectors and take unit cross product.
255  Node v1 = *(node[1]) - *(node[0]);
256  Node v2 = *(node[2]) - *(node[0]);
257  UnitCrossProd(v1, v2, n);
258  }
259  else
260  {
261  // Calculate gradient vector and invert.
262  Node dx = *(node[1]) - *(node[0]);
263  dx /= sqrt(dx.abs2());
264  n.m_x = -dx.m_y;
265  n.m_y = dx.m_x;
266  n.m_z = 0;
267  }
268 
269  // Insert face normal into vertex normal list or add to existing
270  // value.
271  for (int j = 0; j < nV; ++j)
272  {
273  nIt = mesh->m_vertexNormals.find(e->GetVertex(j)->m_id);
274  if (nIt == mesh->m_vertexNormals.end())
275  {
276  mesh->m_vertexNormals[e->GetVertex(j)->m_id] = n;
277  }
278  else
279  {
280  nIt->second += n;
281  }
282  }
283  }
284 
285  // Normalize resulting vectors.
286  for (nIt = mesh->m_vertexNormals.begin();
287  nIt != mesh->m_vertexNormals.end (); ++nIt)
288  {
289  Node &n = mesh->m_vertexNormals[nIt->first];
290  n /= sqrt(n.abs2());
291  }
292  }
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:161
boost::shared_ptr< Element > ElementSharedPtr
Shared pointer to an element.
Definition: MeshElements.h:63
void UnitCrossProd(Node &a, Node &b, Node &c)
StandardMatrixTag boost::call_traits< LhsDataType >::const_reference rhs typedef NekMatrix< LhsDataType, StandardMatrixTag >::iterator iterator
void Nektar::Utilities::ProcessSpherigon::Process ( )
virtual

Write mesh to output file.

Perform the spherigon smoothing technique on the mesh.

Implements Nektar::Utilities::Module.

Definition at line 297 of file ProcessSpherigon.cpp.

References Nektar::Utilities::Node::abs2(), Nektar::MemoryManager< DataType >::AllocateSharedPtr(), ASSERTL0, ASSERTL1, CrossProdMag(), Nektar::Utilities::Node::dot(), Nektar::LibUtilities::eGaussLobattoLegendre, Nektar::LibUtilities::eGaussRadauMAlpha1Beta0, Nektar::LibUtilities::eModified_A, Nektar::LibUtilities::eNodalTriElec, Nektar::LibUtilities::eOrtho_A, Nektar::LibUtilities::eOrtho_B, Nektar::LibUtilities::eQuadrilateral, Nektar::LibUtilities::eSegment, Nektar::LibUtilities::eTriangle, GenerateNormals(), Nektar::ParseUtils::GenerateSeqVector(), Nektar::ParseUtils::GenerateUnOrderedVector(), Nektar::Utilities::GetElementFactory(), Nektar::iterator, Nektar::Utilities::Module::m_config, Nektar::Utilities::Module::m_mesh, Nektar::Utilities::Node::m_x, Nektar::Utilities::Node::m_y, Nektar::Utilities::Node::m_z, SuperBlend(), and Vmath::Zero().

298  {
299  ASSERTL0(m_mesh->m_spaceDim == 3 || m_mesh->m_spaceDim == 2,
300  "Spherigon implementation only valid in 2D/3D.");
301 
303  boost::unordered_set<int> visitedEdges;
304 
305  // First construct vector of elements to process.
306  vector<ElementSharedPtr> el;
307 
308  if (m_mesh->m_verbose)
309  {
310  cout << "ProcessSpherigon: Smoothing mesh..." << endl;
311  }
312 
313  if (m_mesh->m_expDim == 2 && m_mesh->m_spaceDim == 3)
314  {
315  // Manifold case - copy expansion dimension.
316  el = m_mesh->m_element[m_mesh->m_expDim];
317  }
318  else if (m_mesh->m_spaceDim == m_mesh->m_expDim)
319  {
320  // Full 2D or 3D case - iterate over stored edges/faces and
321  // create segments/triangles representing those edges/faces.
322  set<pair<int,int> >::iterator it;
323  vector<int> t;
324  t.push_back(0);
325 
326 
327  // Construct list of spherigon edges/faces from a tag.
328  string surfTag = m_config["surf"].as<string>();
329  bool prismTag = m_config["BothTriFacesOnPrism"].beenSet;
330 
331  if (surfTag != "")
332  {
333  vector<unsigned int> surfs;
334  ParseUtils::GenerateSeqVector(surfTag.c_str(), surfs);
335  sort(surfs.begin(), surfs.end());
336 
337  m_mesh->m_spherigonSurfs.clear();
338  for (int i = 0; i < m_mesh->m_element[m_mesh->m_expDim].size(); ++i)
339  {
340  ElementSharedPtr el = m_mesh->m_element[m_mesh->m_expDim][i];
341  int nSurf = m_mesh->m_expDim == 3 ? el->GetFaceCount() :
342  el->GetEdgeCount();
343 
344  for (int j = 0; j < nSurf; ++j)
345  {
346  int bl = el->GetBoundaryLink(j);
347  if (bl == -1)
348  {
349  continue;
350  }
351 
352  ElementSharedPtr bEl = m_mesh->m_element[m_mesh->m_expDim-1][bl];
353  vector<int> tags = bEl->GetTagList();
354  vector<int> inter;
355 
356  sort(tags.begin(), tags.end());
357  set_intersection(surfs.begin(), surfs.end(),
358  tags .begin(), tags .end(),
359  back_inserter(inter));
360 
361  if (inter.size() == 1)
362  {
363  m_mesh->m_spherigonSurfs.insert(make_pair(i, j));
364 
365  // Curve other tri face on Prism. Note could be
366  // problem on pyramid when implemented.
367  if(nSurf == 5 && prismTag)
368  {
369  // add other end of prism on boundary for
370  // smoothing
371  int triFace = j == 1 ? 3 : 1;
372  m_mesh->m_spherigonSurfs.insert(
373  make_pair(i, triFace));
374  }
375  }
376  }
377  }
378  }
379 
380  if (m_mesh->m_spherigonSurfs.size() == 0)
381  {
382  cerr << "WARNING: Spherigon surfaces have not been defined "
383  << "-- ignoring smoothing." << endl;
384  return;
385  }
386 
387  if (m_mesh->m_expDim == 3)
388  {
389  for (it = m_mesh->m_spherigonSurfs.begin();
390  it != m_mesh->m_spherigonSurfs.end (); ++it)
391  {
392  FaceSharedPtr f = m_mesh->m_element[m_mesh->m_expDim][it->first]->
393  GetFace(it->second);
394  vector<NodeSharedPtr> nodes = f->m_vertexList;
395  LibUtilities::ShapeType eType = (LibUtilities::ShapeType)(nodes.size());
396  ElmtConfig conf(eType, 1, false, false);
397 
398  // Create 2D element.
400  CreateInstance(eType,conf,nodes,t);
401 
402  // Copy vertices/edges from face.
403  for (int i = 0; i < f->m_vertexList.size(); ++i)
404  {
405  elmt->SetVertex(i, f->m_vertexList[i]);
406  }
407  for (int i = 0; i < f->m_edgeList.size(); ++i)
408  {
409  elmt->SetEdge(i, f->m_edgeList[i]);
410  }
411 
412  el.push_back(elmt);
413  }
414  }
415  else
416  {
417  for (it = m_mesh->m_spherigonSurfs.begin();
418  it != m_mesh->m_spherigonSurfs.end (); ++it)
419  {
420  EdgeSharedPtr edge = m_mesh->m_element[m_mesh->m_expDim][it->first]->
421  GetEdge(it->second);
422  vector<NodeSharedPtr> nodes;
424  ElmtConfig conf(eType, 1, false, false);
425 
426  nodes.push_back(edge->m_n1);
427  nodes.push_back(edge->m_n2);
428 
429  // Create 2D element.
431  CreateInstance(eType,conf,nodes,t);
432 
433  // Copy vertices/edges from original element.
434  elmt->SetVertex(0, nodes[0]);
435  elmt->SetVertex(1, nodes[1]);
436  elmt->SetEdge(0, m_mesh->m_element[m_mesh->m_expDim][it->first]->
437  GetEdge(it->second));
438  el.push_back(elmt);
439  }
440  }
441  }
442  else
443  {
444  ASSERTL0(false, "Spherigon expansions must be 2/3 dimensional");
445  }
446 
447 
448  // See if vertex normals have been generated. If they have not,
449  // approximate them by summing normals of surrounding elements.
450  bool normalsGenerated = false;
451 
452  // Read Normal file if one exists
453  std::string normalfile = m_config["usenormalfile"].as<string>();
454  if(normalfile.compare("NoFile") != 0)
455  {
456  NekDouble scale = m_config["scalefile"].as<NekDouble>();
457 
458  if (m_mesh->m_verbose)
459  {
460  cout << "Inputing normal file: " << normalfile << " with scaling of " << scale << endl;
461  }
462 
463  ifstream inply;
464  InputPlySharedPtr plyfile;
465 
466  inply.open(normalfile.c_str());
467  ASSERTL0(inply,string("Could not open input ply file: ") +
468  normalfile);
469 
470  int j;
471  MeshSharedPtr m = boost::shared_ptr<Mesh>(new Mesh());
472  plyfile = boost::shared_ptr<InputPly>(new InputPly(m));
473  plyfile->ReadPly(inply,scale);
474  plyfile->ProcessVertices();
475 
476  MeshSharedPtr plymesh = plyfile->GetMesh();
477  GenerateNormals(plymesh->m_element[plymesh->m_expDim],plymesh);
478 
479  // finaly find nearest vertex and set normal to mesh surface file normal.
480  // probably should have a hex tree search ?
481  Array<OneD, NekDouble> len2(plymesh->m_vertexSet.size());
482  Node minx(0,0.0,0.0,0.0), tmp,tmpsav;
483  NodeSet::iterator it;
485  map<int,NodeSharedPtr> surfverts;
486 
487  // make a map of normal vertices to visit based on elements el
488  for (int i = 0; i < el.size(); ++i)
489  {
490  ElementSharedPtr e = el[i];
491  int nV = e->GetVertexCount();
492  for (int j = 0; j < nV; ++j)
493  {
494  int id = e->GetVertex(j)->m_id;
495  surfverts[id] = e->GetVertex(j);
496  }
497  }
498 
499  //loop over all element in ply mesh and determine
500  //xmin,xmax,ymin,ymax as search criterion
501 
502 
503  NekDouble mindiff,diff;
504  int cntmin;
505 
506  if (m_mesh->m_verbose)
507  {
508  cout << "\t Processing surface normals " << endl;
509  }
510  int cnt = 0;
511  map<int,int> locnorm;
512  for(vIt = surfverts.begin(); vIt != surfverts.end(); ++vIt,++cnt)
513  {
514  mindiff = 1e12;
515 
516  for(j = 0, it = plymesh->m_vertexSet.begin(); it != plymesh->m_vertexSet.end(); ++it, ++j)
517  {
518  tmp = *(vIt->second)- *(*it);
519  diff = tmp.abs2();
520 
521  if(diff < mindiff)
522  {
523  mindiff = diff;
524  cntmin = (*it)->m_id;
525  tmpsav = tmp;
526  }
527  }
528  locnorm[cntmin] = vIt->first;
529 
530  ASSERTL1(cntmin < plymesh->m_vertexNormals.size(),"cntmin is out of range");
531  m_mesh->m_vertexNormals[vIt->first] = plymesh->m_vertexNormals[cntmin];
532 
533  }
534  if (m_mesh->m_verbose)
535  {
536  cout << "\t end of processing surface normals " << endl;
537  }
538  normalsGenerated = true;
539  }
540  else if (m_mesh->m_vertexNormals.size() == 0)
541  {
543  normalsGenerated = true;
544  }
545 
546 
547  // See if we should add noise to normals
548  std::string normalnoise = m_config["normalnoise"].as<string>();
549  if(normalnoise.compare("NotSpecified") != 0)
550  {
551  vector<NekDouble> values;
552  ASSERTL0(ParseUtils::GenerateUnOrderedVector(normalnoise.c_str(),values),"Failed to interpret normal noise string");
553 
554  int nvalues = values.size()/2;
555  NekDouble amp = values[0];
556 
557 
558  if (m_mesh->m_verbose)
559  {
560  cout << "\t adding noise to normals of amplitude "<< amp << " in range: ";
561  for(int i = 0; i < nvalues; ++i)
562  {
563  cout << values[2*i+1] <<"," << values[2*i+2] << " ";
564  }
565  cout << endl;
566  }
567 
569  map<int,NodeSharedPtr> surfverts;
570 
571  // make a map of normal vertices to visit based on elements el
572  for (int i = 0; i < el.size(); ++i)
573  {
574  ElementSharedPtr e = el[i];
575  int nV = e->GetVertexCount();
576  for (int j = 0; j < nV; ++j)
577  {
578  int id = e->GetVertex(j)->m_id;
579  surfverts[id] = e->GetVertex(j);
580  }
581  }
582 
583  for(vIt = surfverts.begin(); vIt != surfverts.end(); ++vIt)
584  {
585  bool AddNoise = false;
586 
587  for(int i = 0; i < nvalues; ++i)
588  {
589  // check to see if point is in range
590  switch(nvalues)
591  {
592  case 1:
593  {
594  if(((vIt->second)->m_x > values[2*i+1])&&((vIt->second)->m_x < values[2*i+2]))
595  {
596  AddNoise = true;
597  }
598  }
599  break;
600  case 2:
601  {
602  if(((vIt->second)->m_x > values[2*i+1])&&
603  ((vIt->second)->m_x < values[2*i+2])&&
604  ((vIt->second)->m_y > values[2*i+3])&&
605  ((vIt->second)->m_y < values[2*i+4]))
606  {
607  AddNoise = true;
608  }
609  }
610  break;
611  case 3:
612  {
613  if(((vIt->second)->m_x > values[2*i+1])&&
614  ((vIt->second)->m_x < values[2*i+2])&&
615  ((vIt->second)->m_y > values[2*i+3])&&
616  ((vIt->second)->m_y < values[2*i+4])&&
617  ((vIt->second)->m_z > values[2*i+5])&&
618  ((vIt->second)->m_z < values[2*i+6]))
619 
620  {
621  AddNoise = true;
622  }
623  }
624  break;
625  }
626 
627  if(AddNoise)
628  {
629  // generate random unit vector;
630  Node rvec(0,rand(),rand(),rand());
631  rvec *= values[0]/sqrt(rvec.abs2());
632 
633  Node normal = m_mesh->m_vertexNormals[vIt->first];
634 
635  normal += rvec;
636  normal /= sqrt(normal.abs2());
637 
638  m_mesh->m_vertexNormals[vIt->first] = normal;
639  }
640  }
641  }
642  }
643 
644 
645  // Allocate storage for interior points.
646  int nq = m_config["N"].as<int>();
647  int nquad = m_mesh->m_spaceDim == 3 ? nq*nq : nq;
648  Array<OneD, NekDouble> x(nq*nq);
649  Array<OneD, NekDouble> y(nq*nq);
650  Array<OneD, NekDouble> z(nq*nq);
651 
652  Array<OneD, NekDouble> xc(nq*nq);
653  Array<OneD, NekDouble> yc(nq*nq);
654  Array<OneD, NekDouble> zc(nq*nq);
655 
656  ASSERTL0(nq > 2, "Number of points must be greater than 2.");
657 
658  LibUtilities::BasisKey B0(
660  LibUtilities::PointsKey(
662  LibUtilities::BasisKey B1(
664  LibUtilities::PointsKey(
669 
670  Array<OneD, NekDouble> xnodal(nq*(nq+1)/2), ynodal(nq*(nq+1)/2);
671  stdtri->GetNodalPoints(xnodal, ynodal);
672 
673  int edgeMap[3][4][2] = {
674  {{0, 1}, {-1, -1}, {-1, -1 }, {-1, -1}}, // seg
675  {{0, 1}, {nq-1, nq}, {nq*(nq-1), -nq}, {-1, -1}}, // tri
676  {{0, 1}, {nq-1, nq}, {nq*nq-1, -1 }, {nq*(nq-1), -nq}} // quad
677  };
678 
679  int vertMap[3][4][2] = {
680  {{0, 1}, {0, 0}, {0, 0}, {0, 0}}, // seg
681  {{0, 1}, {1, 2}, {2, 3}, {0, 0}}, // tri
682  {{0, 1}, {1, 2}, {2, 3}, {3, 0}}, // quad
683  };
684 
685  for (int i = 0; i < el.size(); ++i)
686  {
687  // Construct a Nektar++ element to obtain coordinate points
688  // inside the element. TODO: Add options for various
689  // nodal/tensor point distributions + number of points to add.
690  ElementSharedPtr e = el[i];
691 
692  LibUtilities::BasisKey B2(
694  LibUtilities::PointsKey(
696 
697  if (e->GetConf().m_e == LibUtilities::eSegment)
698  {
700  boost::dynamic_pointer_cast<SpatialDomains::SegGeom>(
701  e->GetGeom(m_mesh->m_spaceDim));
704  B2, geom);
705  seg->GetCoords(x,y,z);
706  nquad = nq;
707  }
708  else if (e->GetConf().m_e == LibUtilities::eTriangle)
709  {
711  boost::dynamic_pointer_cast<SpatialDomains::TriGeom>(
712  e->GetGeom(3));
716  B0, B1, LibUtilities::eNodalTriElec, geom);
717 
718  Array<OneD, NekDouble> coord(2);
719  tri->GetCoords(xc,yc,zc);
720  nquad = nq*(nq+1)/2;
721 
722  for (int j = 0; j < nquad; ++j)
723  {
724  coord[0] = xnodal[j];
725  coord[1] = ynodal[j];
726  x[j] = stdtri->PhysEvaluate(coord, xc);
727  }
728 
729  for (int j = 0; j < nquad; ++j)
730  {
731  coord[0] = xnodal[j];
732  coord[1] = ynodal[j];
733  y[j] = stdtri->PhysEvaluate(coord, yc);
734  }
735 
736  for (int j = 0; j < nquad; ++j)
737  {
738  coord[0] = xnodal[j];
739  coord[1] = ynodal[j];
740  z[j] = stdtri->PhysEvaluate(coord, zc);
741  }
742  }
743  else if (e->GetConf().m_e == LibUtilities::eQuadrilateral)
744  {
746  boost::dynamic_pointer_cast<SpatialDomains::QuadGeom>(
747  e->GetGeom(3));
750  B2, B2, geom);
751  quad->GetCoords(x,y,z);
752  nquad = nq*nq;
753  }
754  else
755  {
756  ASSERTL0(false, "Unknown expansion type.");
757  }
758 
759  // Zero z-coordinate in 2D.
760  if (m_mesh->m_spaceDim == 2)
761  {
762  Vmath::Zero(nquad, z, 1);
763  }
764 
765  // Find vertex normals.
766  int nV = e->GetVertexCount();
767  vector<Node> v, vN;
768  for (int j = 0; j < nV; ++j)
769  {
770  v.push_back(*(e->GetVertex(j)));
771  ASSERTL1(m_mesh->m_vertexNormals.count(v[j].m_id) != 0,"Normal has not been defined");
772  vN.push_back(m_mesh->m_vertexNormals[v[j].m_id]);
773  }
774 
775  vector<Node> tmp (nV);
776  vector<double> r (nV);
777  vector<Node> K (nV);
778  vector<Node> Q (nV);
779  vector<Node> Qp (nV);
780  vector<double> blend(nV);
781  vector<Node> out (nquad);
782 
783  // Calculate segment length for 2D spherigon routine.
784  double segLength = sqrt((v[0] - v[1]).abs2());
785 
786  // Perform Spherigon method to smooth manifold.
787  for (int j = 0; j < nquad; ++j)
788  {
789  Node P(0, x[j], y[j], z[j]);
790  Node N(0,0,0,0);
791 
792  // Calculate generalised barycentric coordinates r[] and the
793  // Phong normal N = vN . r for this point of the element.
794  if (m_mesh->m_spaceDim == 2)
795  {
796  // In 2D the coordinates are given by a ratio of the
797  // segment length to the distance from one of the
798  // endpoints.
799  r[0] = sqrt((P - v[0]).abs2()) / segLength;
800  r[0] = max(min(1.0, r[0]), 0.0);
801  r[1] = 1.0 - r[0];
802 
803  // Calculate Phong normal.
804  N = vN[0]*r[0] + vN[1]*r[1];
805  }
806  else if (m_mesh->m_spaceDim == 3)
807  {
808  for (int k = 0; k < nV; ++k)
809  {
810  tmp[k] = P - v[k];
811  }
812 
813  // Calculate generalized barycentric coordinate system
814  // (see equation 6 of paper).
815  double weight = 0.0;
816  for (int k = 0; k < nV; ++k)
817  {
818  r[k] = 1.0;
819  for (int l = 0; l < nV-2; ++l)
820  {
821  r[k] *= CrossProdMag(tmp[(k+l+1) % nV],
822  tmp[(k+l+2) % nV]);
823  }
824  weight += r[k];
825  }
826 
827  // Calculate Phong normal (equation 1).
828  for (int k = 0; k < nV; ++k)
829  {
830  r[k] /= weight;
831  N += vN[k]*r[k];
832  }
833  }
834 
835  // Normalise Phong normal.
836  N /= sqrt(N.abs2());
837 
838  for (int k = 0; k < nV; ++k)
839  {
840  // Perform steps denoted in equations 2, 3, 8 for C1
841  // smoothing.
842  double tmp1;
843  K [k] = P+N*((v[k]-P).dot(N));
844  tmp1 = (v[k]-K[k]).dot(vN[k]) / (1.0 + N.dot(vN[k]));
845  Q [k] = K[k] + N*tmp1;
846  Qp[k] = v[k] - N*((v[k]-P).dot(N));
847  }
848 
849  // Apply C1 blending function to the surface. TODO: Add
850  // option to do (more efficient) C0 blending function.
851  SuperBlend(r, Qp, P, blend);
852  P.m_x = P.m_y = P.m_z = 0.0;
853 
854  // Apply blending (equation 4).
855  for (int k = 0; k < nV; ++k)
856  {
857  P += Q[k]*blend[k];
858  }
859 
860  out[j] = P;
861  }
862 
863  // Push nodes into lines - TODO: face interior nodes.
864  // offset = 0 (seg), 1 (tri) or 2 (quad)
865  int offset = (int)e->GetConf().m_e-2;
866 
867  for (int edge = 0; edge < e->GetEdgeCount(); ++edge)
868  {
869  eIt = visitedEdges.find(e->GetEdge(edge)->m_id);
870  if (eIt == visitedEdges.end())
871  {
872  bool reverseEdge = !(v[vertMap[offset][edge][0]] ==
873  *(e->GetEdge(edge)->m_n1));
874 
875  // Clear existing curvature.
876  e->GetEdge(edge)->m_edgeNodes.clear();
877 
878  if (e->GetConf().m_e != LibUtilities::eTriangle)
879  {
880  for (int j = 1; j < nq-1; ++j)
881  {
882  int v = edgeMap[offset][edge][0] +
883  j*edgeMap[offset][edge][1];
884  e->GetEdge(edge)->m_edgeNodes.push_back(
885  NodeSharedPtr(new Node(out[v])));
886  }
887  }
888  else
889  {
890  for (int j = 0; j < nq-2; ++j)
891  {
892  int v = 3 + edge*(nq-2) + j;
893  e->GetEdge(edge)->m_edgeNodes.push_back(
894  NodeSharedPtr(new Node(out[v])));
895  }
896  }
897 
898  if (reverseEdge)
899  {
900  reverse(e->GetEdge(edge)->m_edgeNodes.begin(),
901  e->GetEdge(edge)->m_edgeNodes.end());
902  }
903 
904  e->GetEdge(edge)->m_curveType =
906 
907  visitedEdges.insert(e->GetEdge(edge)->m_id);
908  }
909  }
910 
911  // Add face nodes in manifold and full 3D case, but not for 2D.
912  if (m_mesh->m_spaceDim == 3)
913  {
914  vector<NodeSharedPtr> volNodes;
915 
916  if (e->GetConf().m_e == LibUtilities::eQuadrilateral)
917  {
918  volNodes.resize((nq-2)*(nq-2));
919  for (int j = 1; j < nq-1; ++j)
920  {
921  for (int k = 1; k < nq-1; ++k)
922  {
923  int v = j*nq+k;
924  volNodes[(j-1)*(nq-2)+(k-1)] =
925  NodeSharedPtr(new Node(out[v]));
926  }
927  }
928  }
929  else
930  {
931  for (int j = 3+3*(nq-2); j < nquad; ++j)
932  {
933  volNodes.push_back(NodeSharedPtr(new Node(out[j])));
934  }
935  }
936 
937  e->SetVolumeNodes(volNodes);
938  }
939  }
940 
941  // Copy face nodes back into 3D element faces
942  if (m_mesh->m_expDim == 3)
943  {
944  set<pair<int,int> >::iterator it;
945  int elmt = 0;
946  for (it = m_mesh->m_spherigonSurfs.begin();
947  it != m_mesh->m_spherigonSurfs.end (); ++it, ++elmt)
948  {
949  FaceSharedPtr f = m_mesh->m_element[m_mesh->m_expDim][it->first]->
950  GetFace(it->second);
951 
952  f->m_faceNodes = el[elmt]->GetVolumeNodes();
953  f->m_curveType = f->m_vertexList.size() == 3 ?
956  }
957  }
958 
959  if (normalsGenerated)
960  {
961  m_mesh->m_vertexNormals.clear();
962  }
963  }
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:161
static boost::shared_ptr< DataType > AllocateSharedPtr()
Allocate a shared pointer from the memory pool.
void GenerateNormals(vector< ElementSharedPtr > &el, MeshSharedPtr &mesh)
Generate a set of approximate vertex normals to a surface represented by line segments in 2D and a hy...
boost::shared_ptr< QuadGeom > QuadGeomSharedPtr
Definition: QuadGeom.h:54
boost::shared_ptr< Edge > EdgeSharedPtr
Shared pointer to an edge.
Definition: MeshElements.h:318
map< string, ConfigOption > m_config
List of configuration values.
Principle Modified Functions .
Definition: BasisType.h:49
MeshSharedPtr m_mesh
Mesh object.
boost::shared_ptr< Face > FaceSharedPtr
Shared pointer to a face.
Definition: MeshElements.h:550
boost::shared_ptr< Node > NodeSharedPtr
Shared pointer to a Node.
Definition: MeshElements.h:195
static bool GenerateSeqVector(const char *const str, std::vector< unsigned int > &vec)
Definition: ParseUtils.hpp:78
boost::shared_ptr< StdNodalTriExp > StdNodalTriExpSharedPtr
Gauss Radau pinned at x=-1, .
Definition: PointsType.h:57
boost::shared_ptr< Element > ElementSharedPtr
Shared pointer to an element.
Definition: MeshElements.h:63
Principle Orthogonal Functions .
Definition: BasisType.h:47
void SuperBlend(vector< double > &r, vector< Node > &Q, Node &P, vector< double > &blend)
Calculate the blending function for spherigon implementation.
boost::shared_ptr< SegExp > SegExpSharedPtr
Definition: SegExp.h:255
boost::shared_ptr< SegGeom > SegGeomSharedPtr
Definition: Geometry2D.h:60
boost::shared_ptr< Mesh > MeshSharedPtr
Shared pointer to a mesh.
Principle Orthogonal Functions .
Definition: BasisType.h:46
boost::shared_ptr< QuadExp > QuadExpSharedPtr
Definition: QuadExp.h:284
double CrossProdMag(Node &a, Node &b)
Calculate the magnitude of the cross product .
double NekDouble
boost::shared_ptr< InputPly > InputPlySharedPtr
Definition: InputPly.h:65
StandardMatrixTag boost::call_traits< LhsDataType >::const_reference rhs typedef NekMatrix< LhsDataType, StandardMatrixTag >::iterator iterator
boost::shared_ptr< TriGeom > TriGeomSharedPtr
Definition: TriGeom.h:58
void Zero(int n, T *x, const int incx)
Zero vector.
Definition: Vmath.cpp:359
static bool GenerateUnOrderedVector(const char *const str, std::vector< NekDouble > &vec)
Definition: ParseUtils.hpp:127
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode...
Definition: ErrorUtil.hpp:191
ElementFactory & GetElementFactory()
1D Gauss-Lobatto-Legendre quadrature points
Definition: PointsType.h:50
2D Nodal Electrostatic Points on a Triangle
Definition: PointsType.h:68
boost::shared_ptr< NodalTriExp > NodalTriExpSharedPtr
Definition: NodalTriExp.h:379
void Nektar::Utilities::ProcessSpherigon::SuperBlend ( vector< double > &  r,
vector< Node > &  Q,
Node P,
vector< double > &  blend 
)
protected

Calculate the $ C^1 $ blending function for spherigon implementation.

See equation (10) of the paper.

Parameters
rGeneralised barycentric coordinates of the point P.
QVector of vertices denoting this triangle/quad.
PPoint in the triangle to apply blending to.
blendThe resulting blending components for each vertex.

Definition at line 169 of file ProcessSpherigon.cpp.

References TOL_BLEND.

Referenced by Process().

171  {
172  vector<double> tmp(r.size());
173  double totBlend = 0.0;
174  int i;
175  int nV = r.size();
176 
177  for (i = 0; i < nV; ++i)
178  {
179  blend[i] = 0.0;
180  tmp [i] = (Q[i]-P).abs2();
181  }
182 
183  for (i = 0; i < nV; ++i)
184  {
185  int ip = (i+1) % nV, im = (i-1+nV) % nV;
186 
187  if (r[i] > TOL_BLEND && r[i] < (1-TOL_BLEND))
188  {
189  blend[i] = r[i]*r[i]*(
190  r[im]*r[im]*tmp[im]/(tmp[im] + tmp[i]) +
191  r[ip]*r[ip]*tmp[ip]/(tmp[ip] + tmp[i]));
192  totBlend += blend[i];
193  }
194  }
195 
196  for (i = 0; i < nV; ++i)
197  {
198  blend[i] /= totBlend;
199  if (r[i] >= (1-TOL_BLEND))
200  {
201  blend[i] = 1.0;
202  }
203  if (r[i] <= TOL_BLEND)
204  {
205  blend[i] = 0.0;
206  }
207  }
208  }
#define TOL_BLEND
void Nektar::Utilities::ProcessSpherigon::UnitCrossProd ( Node a,
Node b,
Node c 
)
protected

Definition at line 118 of file ProcessSpherigon.cpp.

References Nektar::Utilities::Node::m_x, Nektar::Utilities::Node::m_y, and Nektar::Utilities::Node::m_z.

Referenced by GenerateNormals().

119  {
120  double inv_mag;
121 
122  c.m_x = a.m_y*b.m_z - a.m_z*b.m_y;
123  c.m_y = a.m_z*b.m_x - a.m_x*b.m_z;
124  c.m_z = a.m_x*b.m_y - a.m_y*b.m_x;
125 
126  inv_mag = 1.0/sqrt(c.m_x*c.m_x + c.m_y*c.m_y + c.m_z*c.m_z);
127 
128  c.m_x = c.m_x*inv_mag;
129  c.m_y = c.m_y*inv_mag;
130  c.m_z = c.m_z*inv_mag;
131  }

Member Data Documentation

ModuleKey Nektar::Utilities::ProcessSpherigon::className
static
Initial value:

Definition at line 55 of file ProcessSpherigon.h.