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:
[legend]

Public Member Functions

 ProcessSpherigon (NekMeshUtils::MeshSharedPtr m)
 Default constructor. More...
 
virtual ~ProcessSpherigon ()
 Destructor. More...
 
virtual void Process ()
 Write mesh to output file. More...
 
- Public Member Functions inherited from Nektar::NekMeshUtils::ProcessModule
NEKMESHUTILS_EXPORT ProcessModule (MeshSharedPtr p_m)
 
- Public Member Functions inherited from Nektar::NekMeshUtils::Module
NEKMESHUTILS_EXPORT Module (MeshSharedPtr p_m)
 
NEKMESHUTILS_EXPORT void RegisterConfig (std::string key, std::string value=std::string())
 Register a configuration option with a module. More...
 
NEKMESHUTILS_EXPORT void PrintConfig ()
 Print out all configuration options for a module. More...
 
NEKMESHUTILS_EXPORT void SetDefaults ()
 Sets default configuration options for those which have not been set. More...
 
NEKMESHUTILS_EXPORT MeshSharedPtr GetMesh ()
 
virtual NEKMESHUTILS_EXPORT void ProcessVertices ()
 Extract element vertices. More...
 
virtual NEKMESHUTILS_EXPORT void ProcessEdges (bool ReprocessEdges=true)
 Extract element edges. More...
 
virtual NEKMESHUTILS_EXPORT void ProcessFaces (bool ReprocessFaces=true)
 Extract element faces. More...
 
virtual NEKMESHUTILS_EXPORT void ProcessElements ()
 Generate element IDs. More...
 
virtual NEKMESHUTILS_EXPORT void ProcessComposites ()
 Generate composites. More...
 
virtual NEKMESHUTILS_EXPORT void ClearElementLinks ()
 

Static Public Member Functions

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

Static Public Attributes

static NekMeshUtils::ModuleKey className
 

Protected Member Functions

void GenerateNormals (std::vector< NekMeshUtils::ElementSharedPtr > &el, NekMeshUtils::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...
 
NekDouble CrossProdMag (NekMeshUtils::Node &a, NekMeshUtils::Node &b)
 Calculate the magnitude of the cross product \( \vec{a}\times\vec{b} \). More...
 
void UnitCrossProd (NekMeshUtils::Node &a, NekMeshUtils::Node &b, NekMeshUtils::Node &c)
 
NekDouble Blend (NekDouble r)
 Calculate the \( C^0 \) blending function for spherigon implementation. More...
 
void SuperBlend (std::vector< NekDouble > &r, std::vector< NekMeshUtils::Node > &Q, NekMeshUtils::Node &P, std::vector< NekDouble > &blend)
 Calculate the \( C^1 \) blending function for spherigon implementation. More...
 
void FindNormalFromPlyFile (NekMeshUtils::MeshSharedPtr &plymesh, std::map< int, NekMeshUtils::NodeSharedPtr > &surfverts)
 
- Protected Member Functions inherited from Nektar::NekMeshUtils::Module
NEKMESHUTILS_EXPORT void ReorderPrisms (PerMap &perFaces)
 Reorder node IDs so that prisms and tetrahedra are aligned correctly. More...
 
NEKMESHUTILS_EXPORT void PrismLines (int prism, PerMap &perFaces, std::set< int > &prismsDone, std::vector< ElementSharedPtr > &line)
 

Additional Inherited Members

- Protected Attributes inherited from Nektar::NekMeshUtils::Module
MeshSharedPtr m_mesh
 Mesh object. More...
 
std::map< std::string, ConfigOptionm_config
 List of configuration values. 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

◆ ProcessSpherigon()

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

Default constructor.

Definition at line 98 of file ProcessSpherigon.cpp.

References Nektar::NekMeshUtils::Module::m_config.

98  : ProcessModule(m)
99 {
100  m_config["N"] =
101  ConfigOption(false, "5", "Number of points to add to face edges.");
102  m_config["surf"] =
103  ConfigOption(false, "-1", "Tag identifying surface to process.");
104  m_config["BothTriFacesOnPrism"] = ConfigOption(
105  true, "-1", "Curve both triangular faces of prism on boundary.");
106  m_config["usenormalfile"] = ConfigOption(
107  false, "NoFile", "Use alternative file for Spherigon definition");
108  m_config["scalefile"] = ConfigOption(
109  false, "1.0", "Apply scaling factor to coordinates in file ");
110  m_config["normalnoise"] =
111  ConfigOption(false,
112  "NotSpecified",
113  "Add randowm noise to normals of amplitude AMP "
114  "in specified region. input string is "
115  "Amp,xmin,xmax,ymin,ymax,zmin,zmax");
116 }
Represents a command-line configuration option.
NEKMESHUTILS_EXPORT ProcessModule(MeshSharedPtr p_m)
std::map< std::string, ConfigOption > m_config
List of configuration values.

◆ ~ProcessSpherigon()

Nektar::Utilities::ProcessSpherigon::~ProcessSpherigon ( )
virtual

Destructor.

Definition at line 121 of file ProcessSpherigon.cpp.

122 {
123 }

Member Function Documentation

◆ Blend()

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

Calculate the \( C^0 \) blending function for spherigon implementation.

See equation (5) of the paper.

Parameters
rBarycentric coordinate.

Definition at line 164 of file ProcessSpherigon.cpp.

165 {
166  return r * r;
167 }

◆ create()

static std::shared_ptr<Module> Nektar::Utilities::ProcessSpherigon::create ( NekMeshUtils::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 std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.

◆ CrossProdMag()

double Nektar::Utilities::ProcessSpherigon::CrossProdMag ( NekMeshUtils::Node a,
NekMeshUtils::Node b 
)
protected

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

Definition at line 148 of file ProcessSpherigon.cpp.

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

Referenced by Process().

149 {
150  double tmp1 = a.m_y * b.m_z - a.m_z * b.m_y;
151  double tmp2 = a.m_z * b.m_x - a.m_x * b.m_z;
152  double tmp3 = a.m_x * b.m_y - a.m_y * b.m_x;
153  return sqrt(tmp1 * tmp1 + tmp2 * tmp2 + tmp3 * tmp3);
154 }

◆ FindNormalFromPlyFile()

void Nektar::Utilities::ProcessSpherigon::FindNormalFromPlyFile ( NekMeshUtils::MeshSharedPtr plymesh,
std::map< int, NekMeshUtils::NodeSharedPtr > &  surfverts 
)
protected

Definition at line 223 of file ProcessSpherigon.cpp.

References ASSERTL1, Nektar::NekMeshUtils::Module::m_mesh, and Nektar::LibUtilities::PrintProgressbar().

Referenced by Process().

225 {
226  int cnt = 0;
227  int j = 0;
228  int prog=0,cntmin;
229 
230  typedef bg::model::point<NekDouble, 3, bg::cs::cartesian> Point;
231  typedef pair<Point, unsigned int> PointI;
232 
233  int n_neighbs = 1;
234 
235  map<int,int> TreeidtoPlyid;
236 
237  //Fill vertex array into tree format
238  vector<PointI> dataPts;
239  for (auto &it : plymesh->m_vertexSet)
240  {
241  dataPts.push_back(make_pair(Point(it->m_x, it->m_y, it->m_z), j));
242  TreeidtoPlyid[j++] = it->m_id;
243  }
244 
245  //Build tree
246  bgi::rtree<PointI, bgi::rstar<16> > rtree;
247  rtree.insert(dataPts.begin(), dataPts.end());
248 
249  //Find neipghbours
250  for (auto &vIt : surfverts)
251  {
252  if(m_mesh->m_verbose)
253  {
254  prog = LibUtilities::PrintProgressbar(cnt,surfverts.size(),
255  "Nearest ply verts",prog);
256  }
257 
258  Point queryPt(vIt.second->m_x, vIt.second->m_y, vIt.second->m_z);
259  vector<PointI> result;
260  rtree.query(bgi::nearest(queryPt, n_neighbs),
261  std::back_inserter(result));
262 
263  cntmin = TreeidtoPlyid[result[0].second];
264 
265  ASSERTL1(cntmin < plymesh->m_vertexNormals.size(),
266  "cntmin is out of range");
267 
268  m_mesh->m_vertexNormals[vIt.first] =
269  plymesh->m_vertexNormals[cntmin];
270  ++cnt;
271  }
272 }
int PrintProgressbar(const int position, const int goal, const std::string message, int lastprogress=-1)
Prints a progressbar.
Definition: Progressbar.hpp:67
A 0-dimensional vertex.
Definition: Point.h:48
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode...
Definition: ErrorUtil.hpp:250

◆ GenerateNormals()

void Nektar::Utilities::ProcessSpherigon::GenerateNormals ( std::vector< NekMeshUtils::ElementSharedPtr > &  el,
NekMeshUtils::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 287 of file ProcessSpherigon.cpp.

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

Referenced by Process().

289 {
290  for (int i = 0; i < el.size(); ++i)
291  {
292  ElementSharedPtr e = el[i];
293 
294  // Ensure that element is a line, triangle or quad.
295  ASSERTL0(e->GetConf().m_e == LibUtilities::eSegment ||
296  e->GetConf().m_e == LibUtilities::eTriangle ||
297  e->GetConf().m_e == LibUtilities::eQuadrilateral,
298  "Spherigon expansions must be lines, triangles or "
299  "quadrilaterals.");
300 
301  // Calculate normal for this element.
302  int nV = e->GetVertexCount();
303  vector<NodeSharedPtr> node(nV);
304 
305  for (int j = 0; j < nV; ++j)
306  {
307  node[j] = e->GetVertex(j);
308  }
309 
310  Node n;
311 
312  if (mesh->m_spaceDim == 3)
313  {
314  // Create two tangent vectors and take unit cross product.
315  Node v1 = *(node[1]) - *(node[0]);
316  Node v2 = *(node[2]) - *(node[0]);
317  UnitCrossProd(v1, v2, n);
318  }
319  else
320  {
321  // Calculate gradient vector and invert.
322  Node dx = *(node[1]) - *(node[0]);
323  NekDouble diff = dx.abs2();
324  dx /= sqrt(diff);
325  n.m_x = -dx.m_y;
326  n.m_y = dx.m_x;
327  n.m_z = 0;
328  }
329 
330  // Insert face normal into vertex normal list or add to existing
331  // value.
332  for (int j = 0; j < nV; ++j)
333  {
334  auto nIt = mesh->m_vertexNormals.find(e->GetVertex(j)->m_id);
335  if (nIt == mesh->m_vertexNormals.end())
336  {
337  mesh->m_vertexNormals[e->GetVertex(j)->m_id] = n;
338  }
339  else
340  {
341  nIt->second += n;
342  }
343  }
344  }
345 
346  // Normalize resulting vectors.
347  for (auto &nIt : mesh->m_vertexNormals)
348  {
349  Node &n = nIt.second;
350  n /= sqrt(n.abs2());
351  }
352 
353 }
void UnitCrossProd(NekMeshUtils::Node &a, NekMeshUtils::Node &b, NekMeshUtils::Node &c)
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:216
NEKMESHUTILS_EXPORT NekDouble abs2() const
Definition: Node.h:156
NekDouble m_y
Y-coordinate.
Definition: Node.h:410
Represents a point in the domain.
Definition: Node.h:62
std::shared_ptr< Element > ElementSharedPtr
Definition: Edge.h:49
double NekDouble
NekDouble m_x
X-coordinate.
Definition: Node.h:408
NekDouble m_z
Z-coordinate.
Definition: Node.h:412

◆ Process()

void Nektar::Utilities::ProcessSpherigon::Process ( )
virtual

Write mesh to output file.

Perform the spherigon smoothing technique on the mesh.

Implements Nektar::NekMeshUtils::Module.

Definition at line 358 of file ProcessSpherigon.cpp.

References Nektar::NekMeshUtils::Node::abs2(), Nektar::MemoryManager< DataType >::AllocateSharedPtr(), ASSERTL0, ASSERTL1, Nektar::LibUtilities::NekFactory< tKey, tBase, tParam >::CreateInstance(), CrossProdMag(), Nektar::NekMeshUtils::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, FindNormalFromPlyFile(), GenerateNormals(), Nektar::ParseUtils::GenerateSeqVector(), Nektar::ParseUtils::GenerateVector(), Nektar::NekMeshUtils::GetElementFactory(), Nektar::NekMeshUtils::Module::m_config, Nektar::NekMeshUtils::Module::m_mesh, Nektar::NekMeshUtils::Node::m_x, Nektar::NekMeshUtils::Node::m_y, Nektar::NekMeshUtils::Node::m_z, CG_Iterations::Mesh, class_topology::Node, class_topology::P, SuperBlend(), and Vmath::Zero().

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

◆ SuperBlend()

void Nektar::Utilities::ProcessSpherigon::SuperBlend ( std::vector< NekDouble > &  r,
std::vector< NekMeshUtils::Node > &  Q,
NekMeshUtils::Node P,
std::vector< NekDouble > &  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 180 of file ProcessSpherigon.cpp.

References class_topology::P, and TOL_BLEND.

Referenced by Process().

184 {
185  vector<double> tmp(r.size());
186  double totBlend = 0.0;
187  int i;
188  int nV = r.size();
189 
190  for (i = 0; i < nV; ++i)
191  {
192  blend[i] = 0.0;
193  tmp[i] = (Q[i] - P).abs2();
194  }
195 
196  for (i = 0; i < nV; ++i)
197  {
198  int ip = (i + 1) % nV, im = (i - 1 + nV) % nV;
199 
200  if (r[i] > TOL_BLEND && r[i] < (1 - TOL_BLEND))
201  {
202  blend[i] =
203  r[i] * r[i] * (r[im] * r[im] * tmp[im] / (tmp[im] + tmp[i]) +
204  r[ip] * r[ip] * tmp[ip] / (tmp[ip] + tmp[i]));
205  totBlend += blend[i];
206  }
207  }
208 
209  for (i = 0; i < nV; ++i)
210  {
211  blend[i] /= totBlend;
212  if (r[i] >= (1 - TOL_BLEND))
213  {
214  blend[i] = 1.0;
215  }
216  if (r[i] <= TOL_BLEND)
217  {
218  blend[i] = 0.0;
219  }
220  }
221 }
#define TOL_BLEND

◆ UnitCrossProd()

void Nektar::Utilities::ProcessSpherigon::UnitCrossProd ( NekMeshUtils::Node a,
NekMeshUtils::Node b,
NekMeshUtils::Node c 
)
protected

Definition at line 129 of file ProcessSpherigon.cpp.

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

Referenced by GenerateNormals().

130 {
131  double inv_mag;
132 
133  c.m_x = a.m_y * b.m_z - a.m_z * b.m_y;
134  c.m_y = a.m_z * b.m_x - a.m_x * b.m_z;
135  c.m_z = a.m_x * b.m_y - a.m_y * b.m_x;
136 
137  inv_mag = 1.0 / sqrt(c.m_x * c.m_x + c.m_y * c.m_y + c.m_z * c.m_z);
138 
139  c.m_x = c.m_x * inv_mag;
140  c.m_y = c.m_y * inv_mag;
141  c.m_z = c.m_z * inv_mag;
142 }

Member Data Documentation

◆ className

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

Definition at line 55 of file ProcessSpherigon.h.