Nektar++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
utilities/NekMesh/InputModules/InputNek5000.cpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: InputNek5000.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: Nektar file format converter.
33 //
34 ////////////////////////////////////////////////////////////////////////////////
35 
36 #include <map>
37 #include <vector>
38 #include <sstream>
39 #include <string>
40 
41 #include <boost/algorithm/string.hpp>
44 
45 #include "InputNek5000.h"
46 
47 using namespace std;
48 using namespace Nektar::NekMeshUtils;
49 
50 namespace Nektar
51 {
52 namespace Utilities
53 {
54 
55 ModuleKey InputNek5000::className = GetModuleFactory().RegisterCreatorFunction(
56  ModuleKey(eInputModule, "rea5000"),
57  InputNek5000::create, "Reads Nektar rea file.");
58 
59 InputNek5000::InputNek5000(MeshSharedPtr m) : InputModule(m)
60 {
61 }
62 
64 {
65 }
66 
67 /**
68  * @brief Processes Nek5000 file format.
69  *
70  * Nek5000 sessions are defined by rea files, and contain sections defining a
71  * DNS simulation in a specific order. The converter only reads mesh
72  * information, curve information if it exists and boundary information. The
73  * format is similar to the rea format supported by #InputNek, but the layout is
74  * sufficiently different that this module is separate.
75  */
77 {
78  // Open the file stream.
79  OpenStream();
80 
81  string line, word;
82  int nParam, nElements, nCurves;
83  int i, j, k, nodeCounter = 0;
84  int nComposite = 1;
86  double vertex[8][3];
87 
88  m_mesh->m_expDim = 0;
89  m_mesh->m_spaceDim = 0;
90 
91  if (m_mesh->m_verbose)
92  {
93  cout << "InputNek5000: Start reading file..." << endl;
94  }
95 
96  // -- Read in parameters.
97 
98  // Ignore first 3 lines. 4th line contains number of parameters.
99  for (i = 0; i < 4; ++i)
100  {
101  getline(m_mshFile, line);
102  }
103 
104  stringstream s(line);
105  s >> nParam;
106 
107  for (i = 0; i < nParam; ++i)
108  {
109  string tmp1, tmp2;
110  getline(m_mshFile, line);
111  s.str(line);
112  s >> tmp1 >> tmp2;
113  }
114 
115  // -- Read in passive scalars (ignore)
116  getline(m_mshFile, line);
117  s.clear();
118  s.str(line);
119  s >> j;
120  for (i = 0; i < j; ++i)
121  {
122  getline(m_mshFile, line);
123  }
124 
125  // -- Read in logical switches (ignore)
126  getline(m_mshFile, line);
127  s.clear();
128  s.str(line);
129  s >> j;
130  for (i = 0; i < j; ++i)
131  {
132  getline(m_mshFile, line);
133  }
134 
135  // -- Read in mesh data.
136 
137  // First hunt for MESH tag
138  bool foundMesh = false;
139  while (!m_mshFile.eof())
140  {
141  getline(m_mshFile, line);
142  if (line.find("MESH") != string::npos)
143  {
144  foundMesh = true;
145  break;
146  }
147  }
148 
149  if (!foundMesh)
150  {
151  cerr << "Couldn't find MESH tag inside file." << endl;
152  abort();
153  }
154 
155  // Now read in number of elements and space dimension.
156  getline(m_mshFile, line);
157  s.clear();
158  s.str(line);
159  s >> nElements >> m_mesh->m_expDim;
160  m_mesh->m_spaceDim = m_mesh->m_expDim;
161 
162  // Set up field names.
163  m_mesh->m_fields.push_back("u");
164  m_mesh->m_fields.push_back("v");
165  if (m_mesh->m_spaceDim > 2)
166  {
167  m_mesh->m_fields.push_back("w");
168  }
169  m_mesh->m_fields.push_back("p");
170 
171  // Loop over and create elements.
172  for (i = 0; i < nElements; ++i)
173  {
174  int nNodes;
175  getline(m_mshFile, line);
176 
177  if (m_mesh->m_expDim == 2)
178  {
179  // - quad: 2 lines with x-coords, y-coords
181  nNodes = 4;
182  for (j = 0; j < 2; ++j)
183  {
184  getline(m_mshFile, line);
185  s.clear();
186  s.str(line);
187  for (k = 0; k < 4; ++k)
188  {
189  s >> vertex[k][j];
190  }
191  }
192  }
193  else
194  {
195  // - hex: 3 lines with x/y/z-coords for base 4 nodes, then 3 more
196  // for upper 4 nodes
197  elType = LibUtilities::eHexahedron;
198  nNodes = 8;
199  for (j = 0; j < 6; ++j)
200  {
201  getline(m_mshFile, line);
202  s.clear();
203  s.str(line);
204  int offset = j > 2 ? 4 : 0;
205  for (k = 0; k < 4; ++k)
206  {
207  s >> vertex[offset + k][j % 3];
208  }
209  }
210  }
211 
212  // Nek5000 meshes do not contain a unique list of nodes, so this block
213  // constructs a unique set so that elements can be created with unique
214  // nodes.
215  vector<NodeSharedPtr> nodeList(nNodes);
216  for (k = 0; k < nNodes; ++k)
217  {
218  nodeList[k] = boost::shared_ptr<Node>(
219  new Node(
220  0, vertex[k][0], vertex[k][1], vertex[k][2]));
221  pair<NodeSet::iterator, bool> testIns =
222  m_mesh->m_vertexSet.insert(nodeList[k]);
223 
224  if (!testIns.second)
225  {
226  nodeList[k] = *(testIns.first);
227  }
228  else
229  {
230  nodeList[k]->m_id = nodeCounter++;
231  }
232  }
233 
234  vector<int> tags(1, 0);
235  ElmtConfig conf(elType, 1, false, false);
237  elType, conf, nodeList, tags);
238  m_mesh->m_element[E->GetDim()].push_back(E);
239  }
240 
241  // -- Read in curved data.
242  getline(m_mshFile, line);
243  if (line.find("CURVE") == string::npos)
244  {
245  cerr << "Cannot find curved side data." << endl;
246  abort();
247  }
248 
249  // Read number of curves.
250  getline(m_mshFile, line);
251  s.clear();
252  s.str(line);
253  s >> nCurves;
254 
256  int nq = 6;
258  LibUtilities::PointsManager()[curveType]->GetPoints(rp);
259 
260  // Map to reorder Nek5000 -> Nektar++ edge ordering. Nek5000 has the same
261  // counter-clockwise ordering of edges/vertices; however the vertical
262  // (i.e. t- or xi_3-direction) edges come last.
263  int nek2nekedge[12] = {
264  0, 1, 2, 3, 8, 9, 10, 11, 4, 5, 6, 7
265  };
266 
267  // Map to reorder Nek5000 -> Nektar++ face ordering. Again we have the same
268  // counter-clockwise ordering; however the 4 vertical faces of the hex are
269  // first, followed by bottom face and then top face.
270  int nek2nekface[6] = {
271  1, 2, 3, 4, 0, 5
272  };
273 
274  if (nCurves > 0)
275  {
276  for (i = 0; i < nCurves; ++i)
277  {
278  getline(m_mshFile, line);
279 
280  int elmt, side;
281  NekDouble curveData[5];
282  char curveType;
283 
284  if (nElements < 1000)
285  {
286  // side in first 3 characters, elmt in next 3
287  s.str(line.substr(0, 3));
288  s >> side;
289  s.clear();
290  s.str(line.substr(3, 3));
291  s >> elmt;
292  line = line.substr(6);
293  }
294  else if (nElements < 1000000)
295  {
296  // side in first 2 characters, elmt in next 6
297  s.str(line.substr(0, 2));
298  s >> side;
299  s.clear();
300  s.str(line.substr(2, 6));
301  s >> elmt;
302  line = line.substr(8);
303  }
304  else
305  {
306  // side in first 2 characters, elmt in next 12
307  s.str(line.substr(0, 2));
308  s >> side;
309  s.clear();
310  s.str(line.substr(2, 12));
311  s >> elmt;
312  line = line.substr(14);
313  }
314 
315  s.clear();
316  s.str(line);
317 
318  for (j = 0; j < 5; ++j)
319  {
320  s >> curveData[j];
321  }
322  s >> curveType;
323 
324  elmt--;
325  side--;
326  side = nek2nekedge[side];
327 
328  switch (curveType)
329  {
330  case 'C':
331  {
332  // Apply circular curvature to edges. Nek5000 assumes that
333  // the curvature should be imposed in x-y planes and has no
334  // z-dependence. The following code is adapted from Semtex
335  // (src/mesh.C)
336  NekDouble radius = curveData[0];
337  int convexity = radius < 0 ? -1 : 1;
338  radius = fabs(radius);
339 
340  ElementSharedPtr el =
341  m_mesh->m_element[m_mesh->m_expDim][elmt];
342  EdgeSharedPtr edge = el->GetEdge(side);
343  edge->m_curveType = LibUtilities::eGaussLobattoLegendre;
344 
345  // Assume 2D projection
346  Node P1(*(edge->m_n1)), P2(*(edge->m_n2));
347 
348  if (fabs(P1.m_z - P2.m_z) > 1e-8)
349  {
350  cout << "warning: detected non x-y edge." << endl;
351  }
352  P1.m_z = P2.m_z = 0.0;
353 
354  Node unitNormal, link, centroid, centre;
355  Node midpoint = (P1 + P2)*0.5, dx = P2 - P1;
356  NekDouble l = sqrt(dx.abs2()), sign = 0.0, semiangle = 0.0;
357 
358  unitNormal.m_x = -dx.m_y / l;
359  unitNormal.m_y = dx.m_x / l;
360 
361  if (2.0 * radius < l)
362  {
363  cerr << "failure" << endl;
364  }
365  else
366  {
367  semiangle = asin (0.5 * l / radius);
368  }
369 
370  // Calculate element centroid
371  vector<NodeSharedPtr> elNodes = el->GetVertexList();
372  int nNodes = elNodes.size();
373 
374  for (int i = 0; i < nNodes; ++i)
375  {
376  // Assume 2D projection
377  Node tmp(*elNodes[i]);
378  tmp.m_z = 0.0;
379  centroid += tmp;
380  }
381 
382  centroid /= (NekDouble)nNodes;
383  link = centroid - midpoint;
384  sign = link.dot(unitNormal);
385  sign = convexity * sign / fabs(sign);
386  centre = midpoint + unitNormal * (sign * cos(semiangle) *
387  radius);
388 
389  NekDouble theta1, theta2, dtheta, phi;
390  theta1 = atan2 (P1.m_y - centre.m_y, P1.m_x - centre.m_x);
391  theta2 = atan2 (P2.m_y - centre.m_y, P2.m_x - centre.m_x);
392  dtheta = theta2 - theta1;
393 
394  if (fabs(dtheta) > 2.0*semiangle + 1e-15)
395  {
396  dtheta += (dtheta < 0.0) ? 2.0*M_PI : -2.0*M_PI;
397  }
398 
399  edge->m_edgeNodes.clear();
400 
401  for (j = 1; j < nq-1; ++j) {
402  phi = theta1 + dtheta * 0.5 * (rp[j] + 1.0);
403  NodeSharedPtr asd(new Node(
404  0,
405  centre.m_x + radius * cos(phi),
406  centre.m_y + radius * sin(phi),
407  edge->m_n1->m_z));
408  edge->m_edgeNodes.push_back(asd);
409  }
410  break;
411  }
412  case 's':
413  case 'S':
414  case 'm':
415  case 'M':
416  cerr << "Curve type '" << curveType << "' on side " << side
417  << " of element " << elmt << " is unsupported;"
418  << "will ignore." << endl;
419  break;
420  default:
421  cerr << "Unknown curve type '" << curveType << "' on side "
422  << side << " of element " << elmt << "; will ignore."
423  << endl;
424  break;
425  }
426  }
427  }
428 
429  // Read boundary conditions.
430  getline(m_mshFile, line);
431  getline(m_mshFile, line);
432  if (line.find("BOUNDARY") == string::npos)
433  {
434  cerr << "Cannot find boundary conditions." << endl;
435  abort();
436  }
437 
438  int nSurfaces = 0;
439  boost::unordered_set<pair<int, int> > periodicIn;
440  int periodicInId = -1, periodicOutId = -1;
441 
442  // Boundary conditions: should be precisely nElements * nFaces lines to
443  // read.
444  int lineCnt = 0;
445  int perIn = 0, perOut = 0;
446 
447  while (m_mshFile.good())
448  {
449  getline(m_mshFile, line);
450 
451  // Found a new section. We don't support anything in the rea file beyond
452  // this point so we'll just quit.
453  if (line.find("*") != string::npos)
454  {
455  break;
456  }
457 
459  char bcType;
460  int elmt, side;
461  NekDouble data[5];
462 
463  // type in chars 0-3
464  s.clear();
465  s.str(line.substr(0, 4));
466  s >> bcType;
467 
468  if (nElements < 1000)
469  {
470  // elmt in chars 4-6, side in next 3
471  s.clear();
472  s.str(line.substr(4, 3));
473  s >> elmt;
474  s.clear();
475  s.str(line.substr(7, 3));
476  s >> side;
477  line = line.substr(10);
478  }
479  else if (nElements < 100000)
480  {
481  // elmt in chars 4-8, side in next 1
482  s.clear();
483  s.str(line.substr(4, 5));
484  s >> elmt;
485  s.clear();
486  s.str(line.substr(9, 1));
487  s >> side;
488  line = line.substr(10);
489  }
490  else if (nElements < 1000000)
491  {
492  // elmt in chars 4-9, no side
493  s.clear();
494  s.str(line.substr(4, 6));
495  s >> elmt;
496  side = lineCnt % (2 * m_mesh->m_expDim);
497  line = line.substr(9);
498  }
499  else
500  {
501  // elmt in chars 4-15, no side
502  s.clear();
503  s.str(line.substr(4, 12));
504  s >> elmt;
505  side = lineCnt % (2 * m_mesh->m_expDim);
506  line = line.substr(15);
507  }
508 
509  s.clear();
510  s.str(line);
511 
512  for (i = 0; i < 5; ++i)
513  {
514  s >> data[i];
515  }
516 
517  // Our ordering starts from 0, not 1.
518  --elmt;
519  --side;
520 
521  // Increment lines read
522  lineCnt++;
523 
524  ElementSharedPtr el = m_mesh->m_element[m_mesh->m_spaceDim][elmt];
525 
526  std::string fields[] = { "u", "v", "w", "p" };
527 
528  switch (bcType)
529  {
530  case 'E':
531  // Edge/face connectivity; ignore since we already have this, at
532  // least for conformal meshes.
533  continue;
534 
535  case 'W':
536  {
537  for (i = 0; i < m_mesh->m_fields.size() - 1; ++i)
538  {
539  c->field.push_back(fields[i]);
540  c->value.push_back("0");
541  c->type.push_back(eDirichlet);
542  }
543 
544  // Set high-order boundary condition for wall.
545  c->field.push_back(fields[3]);
546  c->value.push_back("0");
547  c->type.push_back(eHOPCondition);
548  break;
549  }
550 
551  case 'P':
552  {
553  // Determine periodic element and face.
554  int perElmt = (int)(data[0] + 0.5) - 1;
555  int perFace = (int)(data[1] + 0.5) - 1;
556 
557  bool setup = false;
558  if (periodicInId == -1)
559  {
560  periodicInId = m_mesh->m_condition.size();
561  periodicOutId = m_mesh->m_condition.size()+1;
562  setup = true;
563  }
564 
565  bool hasIn = periodicIn.find(make_pair(perElmt, perFace)) !=
566  periodicIn.end();
567 
568  if (hasIn)
569  {
570  swap(periodicInId, periodicOutId);
571  perOut++;
572  }
573  else
574  {
575  periodicIn.insert(make_pair(elmt, side));
576  perIn++;
577  }
578 
579  std::string periodicInStr = "[" +
580  boost::lexical_cast<string>(periodicInId) + "]";
581  std::string periodicOutStr = "[" +
582  boost::lexical_cast<string>(periodicOutId) + "]";
583 
584  for (i = 0; i < m_mesh->m_fields.size() - 1; ++i)
585  {
586  c->field.push_back(fields[i]);
587  c->value.push_back(periodicOutStr);
588  c->type.push_back(ePeriodic);
589  }
590 
591  c->field.push_back(fields[3]);
592  c->value.push_back(periodicOutStr);
593  c->type.push_back(ePeriodic);
594 
595  if (setup)
596  {
599 
600  c->m_composite.push_back(nComposite++);
601  c2->m_composite.push_back(nComposite++);
602 
603  c2->field = c->field;
604  c2->type = c->type;
605  for (i = 0; i < c->type.size(); ++i)
606  {
607  c2->value.push_back(periodicInStr);
608  }
609 
610  m_mesh->m_condition[periodicInId] = c;
611  m_mesh->m_condition[periodicOutId] = c2;
612  }
613 
614  if (hasIn)
615  {
616  swap(periodicInId, periodicOutId);
617  }
618 
619  break;
620  }
621 
622  default:
623  continue;
624  }
625 
626  int compTag, conditionId;
627  ElementSharedPtr surfEl;
628 
629  // Create element for face (3D) or segment (2D).
630  if (el->GetDim() == 3)
631  {
632  FaceSharedPtr f = el->GetFace(nek2nekface[side]);
633  vector<NodeSharedPtr> nodeList;
634  nodeList.insert(nodeList.begin(),
635  f->m_vertexList.begin(),
636  f->m_vertexList.end());
637 
638  vector<int> tags;
639  ElmtConfig conf(
640  LibUtilities::eQuadrilateral, 1, true, true, false,
642  surfEl =
644  conf, nodeList, tags);
645 
646  // Copy high-order surface information from edges.
647  for (int i = 0; i < f->m_vertexList.size(); ++i)
648  {
649  surfEl->GetEdge(i)->m_edgeNodes = f->m_edgeList[i]->m_edgeNodes;
650  surfEl->GetEdge(i)->m_curveType = f->m_edgeList[i]->m_curveType;
651  }
652  }
653  else
654  {
655  EdgeSharedPtr f = el->GetEdge(side);
656 
657  vector<NodeSharedPtr> nodeList;
658  nodeList.push_back(f->m_n1);
659  nodeList.push_back(f->m_n2);
660 
661  vector<int> tags;
662 
663  ElmtConfig conf(
664  LibUtilities::eSegment, 1, true, true, false,
667  LibUtilities::eSegment, conf, nodeList, tags);
668  }
669 
670  // Now attempt to find this boundary condition inside
671  // m_mesh->condition. This is currently a linear search and should
672  // probably be made faster!
674  bool found = false;
675  for (it = m_mesh->m_condition.begin(); it != m_mesh->m_condition.end();
676  ++it)
677  {
678  if (c == it->second)
679  {
680  found = true;
681  c = it->second;
682  break;
683  }
684  }
685 
686  if (!found)
687  {
688  conditionId = m_mesh->m_condition.size();
689  compTag = nComposite;
690  c->m_composite.push_back(compTag);
691  m_mesh->m_condition[conditionId] = c;
692  }
693  else
694  {
695  compTag = c->m_composite[0];
696  }
697 
698  // Insert composite tag into element and insert element into
699  // mesh.
700  vector<int> existingTags = surfEl->GetTagList();
701 
702  existingTags.insert(existingTags.begin(), compTag);
703  surfEl->SetTagList(existingTags);
704  surfEl->SetId(nSurfaces);
705 
706  m_mesh->m_element[surfEl->GetDim()].push_back(surfEl);
707  nSurfaces++;
708  }
709 
710  if (lineCnt != nElements * (m_mesh->m_expDim * 2))
711  {
712  cerr << "Warning: boundary conditions may not have been correctly read "
713  << "from Nek5000 input file." << endl;
714  }
715 
716  if (perIn != perOut)
717  {
718  cerr << "Warning: number of periodic faces does not match." << endl;
719  }
720 
721  m_mshFile.reset();
722 
723  // -- Process rest of mesh.
724  ProcessEdges();
725  ProcessFaces();
726  ProcessElements();
728 
729  // -- Set periodic composites to not be reordered.
730  if (periodicInId != -1)
731  {
732  m_mesh->m_composite[m_mesh->m_condition[periodicInId]
733  ->m_composite[0]]->m_reorder = false;
734  m_mesh->m_composite[m_mesh->m_condition[periodicOutId]
735  ->m_composite[0]]->m_reorder = false;
736  }
737 }
738 
739 }
740 }
NEKMESHUTILS_EXPORT NekDouble dot(const Node &pSrc) const
Definition: Node.h:159
Basic information about an element.
Definition: ElementConfig.h:50
io::filtering_istream m_mshFile
Input stream.
tBaseSharedPtr CreateInstance(tKey idKey BOOST_PP_COMMA_IF(MAX_PARAM) BOOST_PP_ENUM_BINARY_PARAMS(MAX_PARAM, tParam, x))
Create an instance of the class referred to by idKey.
Definition: NekFactory.hpp:162
static boost::shared_ptr< DataType > AllocateSharedPtr()
Allocate a shared pointer from the memory pool.
#define sign(a, b)
return the sign(b)*a
Definition: Polylib.cpp:27
STL namespace.
pair< ModuleType, string > ModuleKey
NekDouble m_y
Y-coordinate.
Definition: Node.h:401
ElementFactory & GetElementFactory()
Definition: Element.cpp:47
Represents a point in the domain.
Definition: Node.h:60
NEKMESHUTILS_EXPORT void OpenStream()
Open a file for input.
virtual NEKMESHUTILS_EXPORT void ProcessFaces(bool ReprocessFaces=true)
Extract element faces.
virtual NEKMESHUTILS_EXPORT void ProcessElements()
Generate element IDs.
boost::shared_ptr< Node > NodeSharedPtr
Definition: Node.h:50
PointsManagerT & PointsManager(void)
Defines a specification for a set of points.
Definition: Points.h:58
double NekDouble
boost::shared_ptr< Condition > ConditionSharedPtr
Definition: Mesh.h:82
boost::shared_ptr< Edge > EdgeSharedPtr
Shared pointer to an edge.
Definition: Edge.h:135
Abstract base class for input modules.
StandardMatrixTag boost::call_traits< LhsDataType >::const_reference rhs typedef NekMatrix< LhsDataType, StandardMatrixTag >::iterator iterator
NekDouble m_x
X-coordinate.
Definition: Node.h:399
boost::shared_ptr< Mesh > MeshSharedPtr
Shared pointer to a mesh.
Definition: Mesh.h:147
virtual NEKMESHUTILS_EXPORT void ProcessEdges(bool ReprocessEdges=true)
Extract element edges.
boost::shared_ptr< Element > ElementSharedPtr
Definition: Edge.h:49
boost::shared_ptr< Face > FaceSharedPtr
Definition: Face.h:148
NekDouble m_z
Z-coordinate.
Definition: Node.h:403
std::pair< ModuleType, std::string > ModuleKey
virtual void Process()
Processes Nek5000 file format.
virtual NEKMESHUTILS_EXPORT void ProcessComposites()
Generate composites.
1D Gauss-Lobatto-Legendre quadrature points
Definition: PointsType.h:52
ModuleFactory & GetModuleFactory()
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, tDescription pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:215