Nektar++
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
MeshElements.h
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: MeshElements.h
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: Mesh manipulation objects.
33 //
34 ////////////////////////////////////////////////////////////////////////////////
35 
36 #ifndef UTILITY_PREPROCESSING_MESHCONVERT_MESHELEMENTS
37 #define UTILITY_PREPROCESSING_MESHCONVERT_MESHELEMENTS
38 
39 #include <vector>
40 #include <string>
41 #include <iostream>
42 #include <iomanip>
43 
44 #include <boost/shared_ptr.hpp>
45 #include <boost/unordered_set.hpp>
46 #include <boost/unordered_map.hpp>
47 
51 
52 #include <SpatialDomains/SegGeom.h>
53 #include <SpatialDomains/TriGeom.h>
55 #include <SpatialDomains/Curve.hpp>
57 
58 namespace Nektar
59 {
60  namespace Utilities
61  {
62  // Forwards declaration for Element class.
63  class Element;
64  /// Shared pointer to an element.
65  typedef boost::shared_ptr<Element> ElementSharedPtr;
66 
67  /**
68  * @brief Represents a point in the domain.
69  *
70  * Such points may either be element vertices, or simply control
71  * points on high-order edges/faces, although this information is not
72  * contained within this class.
73  */
74  class Node {
75  public:
76  /// Create a new node at a specified coordinate.
77  Node(int pId, NekDouble pX, NekDouble pY, NekDouble pZ)
78  : m_id(pId), m_x(pX), m_y(pY), m_z(pZ), m_geom() {}
79  /// Copy an existing node.
80  Node(const Node& pSrc)
81  : m_id(pSrc.m_id), m_x(pSrc.m_x), m_y(pSrc.m_y),
82  m_z(pSrc.m_z), m_geom() {}
83  Node() : m_id(0), m_x(0.0), m_y(0.0), m_z(0.0), m_geom() {}
84  ~Node() {}
85 
86  /// Reset the local id;
87  void SetID(int pId)
88  {
89  m_id = pId;
90  }
91 
92  /// Get the local id;
93  int GetID(void)
94  {
95  return m_id;
96  }
97 
98  /// Define node ordering based on ID.
99  bool operator<(const Node& pSrc)
100  {
101  return (m_id < pSrc.m_id);
102  }
103  /// Define node equality based on coordinate.
104  bool operator==(const Node& pSrc)
105  {
106  return m_x == pSrc.m_x && m_y == pSrc.m_y && m_z == pSrc.m_z;
107  }
108 
109  Node operator+(const Node &pSrc) const
110  {
111  return Node(m_id, m_x+pSrc.m_x, m_y+pSrc.m_y, m_z+pSrc.m_z);
112  }
113 
114  Node operator-(const Node &pSrc) const
115  {
116  return Node(m_id, m_x-pSrc.m_x, m_y-pSrc.m_y, m_z-pSrc.m_z);
117  }
118 
119  Node operator*(const Node &pSrc) const
120  {
121  return Node(m_id, m_x*pSrc.m_x, m_y*pSrc.m_y, m_z*pSrc.m_z);
122  }
123 
124  Node operator*(const NekDouble &alpha) const
125  {
126  return Node(m_id, alpha*m_x, alpha*m_y, alpha*m_z);
127  }
128 
129  Node operator/(const NekDouble &alpha) const
130  {
131  return Node(m_id, m_x/alpha, m_y/alpha, m_z/alpha);
132  }
133 
134  void operator+=(const Node &pSrc)
135  {
136  m_x += pSrc.m_x;
137  m_y += pSrc.m_y;
138  m_z += pSrc.m_z;
139  }
140 
141  void operator*=(const NekDouble &alpha)
142  {
143  m_x *= alpha;
144  m_y *= alpha;
145  m_z *= alpha;
146  }
147 
148  void operator/=(const NekDouble &alpha)
149  {
150  m_x /= alpha;
151  m_y /= alpha;
152  m_z /= alpha;
153  }
154 
155  NekDouble abs2() const
156  {
157  return m_x*m_x+m_y*m_y+m_z*m_z;
158  }
159 
160  NekDouble dot(const Node &pSrc) const
161  {
162  return m_x*pSrc.m_x + m_y*pSrc.m_y + m_z*pSrc.m_z;
163  }
164 
165 
166  Node curl(const Node &pSrc) const
167  {
168  return Node(m_id, m_y*pSrc.m_z - m_z*pSrc.m_y,
169  m_z*pSrc.m_x-m_x*pSrc.m_z, m_x*pSrc.m_y-m_y*pSrc.m_x);
170  }
171 
172  /// Generate a %SpatialDomains::PointGeom for this node.
174  {
178 
179  return ret;
180  }
181 
182  /// ID of node.
183  int m_id;
184  /// X-coordinate.
186  /// Y-coordinate.
188  /// Z-coordinate.
190 
191  private:
193  };
194  /// Shared pointer to a Node.
195  typedef boost::shared_ptr<Node> NodeSharedPtr;
196 
197  bool operator==(NodeSharedPtr const &p1, NodeSharedPtr const &p2);
198  bool operator< (NodeSharedPtr const &p1, NodeSharedPtr const &p2);
199  std::ostream &operator<<(std::ostream &os, const NodeSharedPtr &n);
200 
201  /**
202  * @brief Defines a hash function for nodes.
203  *
204  * The hash of a node is straight-forward; a combination of the x, y,
205  * and z co-ordinates in this order.
206  */
207  struct NodeHash : std::unary_function<NodeSharedPtr, std::size_t>
208  {
209  std::size_t operator()(NodeSharedPtr const& p) const
210  {
211  std::size_t seed = 0;
212  boost::hash_combine(seed, p->m_x);
213  boost::hash_combine(seed, p->m_y);
214  boost::hash_combine(seed, p->m_z);
215  return seed;
216  }
217  };
218  typedef boost::unordered_set<NodeSharedPtr, NodeHash> NodeSet;
219 
220 
221  /**
222  * @brief Represents an edge which joins two points.
223  *
224  * An edge is defined by two nodes (vertices) and, for high-order edges,
225  * a set of control nodes defining the shape of the edge.
226  */
227  class Edge {
228  public:
229  /// Creates a new edge.
230  Edge(NodeSharedPtr pVertex1, NodeSharedPtr pVertex2,
231  std::vector<NodeSharedPtr> pEdgeNodes,
232  LibUtilities::PointsType pCurveType)
233  : m_n1(pVertex1), m_n2(pVertex2), m_edgeNodes(pEdgeNodes),
234  m_curveType(pCurveType), m_geom() {}
235  /// Copies an existing edge.
236  Edge(const Edge& pSrc)
237  : m_n1(pSrc.m_n1), m_n2(pSrc.m_n2), m_edgeNodes(pSrc.m_edgeNodes),
238  m_curveType(pSrc.m_curveType), m_geom(pSrc.m_geom) {}
239  ~Edge() {}
240 
241  /// Returns the total number of nodes defining the edge.
242  unsigned int GetNodeCount() const
243  {
244  return m_edgeNodes.size() + 2;
245  }
246 
247  /// Creates a Nektar++ string listing the coordinates of all the
248  /// nodes.
249  std::string GetXmlCurveString() const
250  {
251  std::stringstream s;
252  std::string str;
253  s << std::scientific << std::setprecision(8) << " "
254  << m_n1->m_x << " " << m_n1->m_y << " " << m_n1->m_z << " ";
255  for (int k = 0; k < m_edgeNodes.size(); ++k) {
256  s << std::scientific << std::setprecision(8) << " "
257  << m_edgeNodes[k]->m_x << " " << m_edgeNodes[k]->m_y
258  << " " << m_edgeNodes[k]->m_z << " ";
259  }
260  s << std::scientific << std::setprecision(8) << " "
261  << m_n2->m_x << " " << m_n2->m_y << " " << m_n2->m_z;
262  return s.str();
263  }
264 
265  /// Generate a SpatialDomains::SegGeom object for this edge.
267  {
268  // Create edge vertices.
271 
272  p[0] = m_n1->GetGeom(coordDim);
273  p[1] = m_n2->GetGeom(coordDim);
274 
275  // Create a curve if high-order information exists.
276  if (m_edgeNodes.size() > 0)
277  {
281 
282  c->m_points.push_back(p[0]);
283  for (int i = 0; i < m_edgeNodes.size(); ++i)
284  {
285  c->m_points.push_back(m_edgeNodes[i]->GetGeom(coordDim));
286  }
287  c->m_points.push_back(p[1]);
288 
290  AllocateSharedPtr(m_id, coordDim, p, c);
291  }
292  else
293  {
295  AllocateSharedPtr(m_id, coordDim, p);
296  }
297 
298  return ret;
299  }
300 
301  /// ID of edge.
302  unsigned int m_id;
303  /// First vertex node.
305  /// Second vertex node.
307  /// List of control nodes between the first and second vertices.
308  std::vector<NodeSharedPtr> m_edgeNodes;
309  /// Distributions of points along edge.
311  /// Element(s) which are linked to this edge.
312  vector<pair<ElementSharedPtr, int> > m_elLink;
313 
314  private:
316  };
317  /// Shared pointer to an edge.
318  typedef boost::shared_ptr<Edge> EdgeSharedPtr;
319 
320  bool operator==(EdgeSharedPtr const &p1, EdgeSharedPtr const &p2);
321  bool operator< (EdgeSharedPtr const &p1, EdgeSharedPtr const &p2);
322 
323  /**
324  * @brief Defines a hash function for edges.
325  *
326  * The hash of an edge is defined using the IDs of the two nodes which
327  * define it. First the minimum ID is hashed, then the maximum
328  * ID, which takes the two possible orientations into account.
329  */
330  struct EdgeHash : std::unary_function<EdgeSharedPtr, std::size_t>
331  {
332  std::size_t operator()(EdgeSharedPtr const& p) const
333  {
334  std::size_t seed = 0;
335  unsigned int id1 = p->m_n1->m_id;
336  unsigned int id2 = p->m_n2->m_id;
337  boost::hash_combine(seed, id1 < id2 ? id1 : id2);
338  boost::hash_combine(seed, id2 < id1 ? id1 : id2);
339  return seed;
340  }
341  };
342  typedef boost::unordered_set<EdgeSharedPtr, EdgeHash> EdgeSet;
343 
344 
345  /**
346  * @brief Represents a face comprised of three or more edges.
347  *
348  * A face is defined by a list of vertices, a list of edges joining
349  * these vertices, and a list of control nodes within the interior of
350  * the face, defining the shape of the face.
351  */
352  class Face {
353  public:
354  /// Create a new face.
355  Face(std::vector<NodeSharedPtr> pVertexList,
356  std::vector<NodeSharedPtr> pFaceNodes,
357  std::vector<EdgeSharedPtr> pEdgeList,
358  LibUtilities::PointsType pCurveType)
359  : m_vertexList(pVertexList),
360  m_edgeList (pEdgeList),
361  m_faceNodes (pFaceNodes),
362  m_curveType (pCurveType),
363  m_geom () {}
364 
365  /// Copy an existing face.
366  Face(const Face& pSrc)
369  m_geom (pSrc.m_geom) {}
370  ~Face() {}
371 
372  /// Equality is defined by matching all vertices.
373  bool operator==(Face& pSrc)
374  {
376  for (it1 = m_vertexList.begin(); it1 != m_vertexList.end(); ++it1)
377  {
378  if (find(pSrc.m_vertexList.begin(), pSrc.m_vertexList.end(), *it1)
379  == pSrc.m_vertexList.end())
380  {
381  return false;
382  }
383  }
384  return true;
385  }
386 
387  /// Returns the total number of nodes (vertices, edge nodes and
388  /// face nodes).
389  unsigned int GetNodeCount() const
390  {
391  unsigned int n = m_faceNodes.size();
392  for (int i = 0; i < m_edgeList.size(); ++i)
393  {
394  n += m_edgeList[i]->GetNodeCount();
395  }
396  n -= m_vertexList.size();
397  return n;
398  }
399 
400  /// Generates a string listing the coordinates of all nodes
401  /// associated with this face.
402  std::string GetXmlCurveString() const
403  {
404  std::stringstream s;
405  std::string str;
406 
407  // Treat 2D point distributions differently to 3D.
411  {
412  vector<NodeSharedPtr> tmp;
413  int n = m_edgeList[0]->GetNodeCount();
414 
415  tmp.insert(tmp.end(), m_vertexList.begin(), m_vertexList.end());
416  for (int k = 0; k < m_edgeList.size(); ++k)
417  {
418  tmp.insert(tmp.end(), m_edgeList[k]->m_edgeNodes.begin(),
419  m_edgeList[k]->m_edgeNodes.end());
420  if (m_edgeList[k]->m_n1 != m_vertexList[k])
421  {
422  // If edge orientation is reversed relative to node
423  // ordering, we need to reverse order of nodes.
424  std::reverse(tmp.begin() + 3 + k*(n-2),
425  tmp.begin() + 3 + (k+1)*(n-2));
426  }
427  }
428  tmp.insert(tmp.end(), m_faceNodes.begin(), m_faceNodes.end());
429 
430  for (int k = 0; k < tmp.size(); ++k) {
431  s << std::scientific << std::setprecision(8) << " "
432  << tmp[k]->m_x << " " << tmp[k]->m_y
433  << " " << tmp[k]->m_z << " ";
434  }
435 
436  return s.str();
437  }
438  else
439  {
440  // Write out in 2D tensor product order.
441  ASSERTL0(m_vertexList.size() == 4,
442  "Face nodes of tensor product only supported "
443  "for quadrilaterals.");
444 
445  int n = (int)sqrt((NekDouble)GetNodeCount());
446  vector<NodeSharedPtr> tmp(n*n);
447 
448  ASSERTL0(n*n == GetNodeCount(), "Wrong number of modes?");
449 
450  // Write vertices
451  tmp[0] = m_vertexList[0];
452  tmp[n-1] = m_vertexList[1];
453  tmp[n*n-1] = m_vertexList[2];
454  tmp[n*(n-1)] = m_vertexList[3];
455 
456  // Write edge-interior
457  int skips[4][2] = {{0,1}, {n-1,n}, {n*(n-1),1}, {0,n}};
458  for (int i = 0; i < 4; ++i)
459  {
460  bool reverseEdge = m_edgeList[i]->m_n1 == m_vertexList[i];
461 
462  if (reverseEdge)
463  {
464  for (int j = n-2; j > 0; --j)
465  {
466  tmp[skips[i][0] + j*skips[i][1]] =
467  m_edgeList[i]->m_edgeNodes[j-1];
468  }
469  }
470  else
471  {
472  for (int j = 1; j < n-1; ++j)
473  {
474  tmp[skips[i][0] + j*skips[i][1]] =
475  m_edgeList[i]->m_edgeNodes[j-1];
476  }
477  }
478  }
479 
480  // Write interior
481  for (int i = 1; i < n-1; ++i)
482  {
483  for (int j = 1; j < n-1; ++j)
484  {
485  tmp[i*n+j] = m_faceNodes[(i-1)*(n-2)+(j-1)];
486  }
487  }
488 
489  for (int k = 0; k < tmp.size(); ++k) {
490  s << std::scientific << std::setprecision(8) << " "
491  << tmp[k]->m_x << " " << tmp[k]->m_y
492  << " " << tmp[k]->m_z << " ";
493  }
494 
495  return s.str();
496  }
497  }
498 
499  /// Generate either SpatialDomains::TriGeom or
500  /// SpatialDomains::QuadGeom for this element.
502  {
503  int nEdge = m_edgeList.size();
504 
507  StdRegions::Orientation edgeo[4];
508 
509  for (int i = 0; i < nEdge; ++i)
510  {
511  edges[i] = m_edgeList[i]->GetGeom(coordDim);
512  }
513 
514  for (int i = 0; i < nEdge; ++i)
515  {
517  *edges[i], *edges[(i+1) % nEdge]);
518  }
519 
520  if (nEdge == 3)
521  {
523  AllocateSharedPtr(m_id, edges, edgeo);
524  }
525  else
526  {
528  AllocateSharedPtr(m_id, edges, edgeo);
529  }
530 
531  return ret;
532  }
533 
534  /// ID of the face.
535  unsigned int m_id;
536  /// List of vertex nodes.
537  std::vector<NodeSharedPtr> m_vertexList;
538  /// List of corresponding edges.
539  std::vector<EdgeSharedPtr> m_edgeList;
540  /// List of face-interior nodes defining the shape of the face.
541  std::vector<NodeSharedPtr> m_faceNodes;
542  /// Distribution of points in this face.
544  /// Element(s) which are linked to this face.
545  vector<pair<ElementSharedPtr, int> > m_elLink;
546 
548  };
549  /// Shared pointer to a face.
550  typedef boost::shared_ptr<Face> FaceSharedPtr;
551 
552  bool operator==(FaceSharedPtr const &p1, FaceSharedPtr const &p2);
553  bool operator< (FaceSharedPtr const &p1, FaceSharedPtr const &p2);
554 
555  struct FaceHash : std::unary_function<FaceSharedPtr, std::size_t>
556  {
557  std::size_t operator()(FaceSharedPtr const& p) const
558  {
559  unsigned int nVert = p->m_vertexList.size();
560  std::size_t seed = 0;
561  std::vector<unsigned int> ids(nVert);
562 
563  for (int i = 0; i < nVert; ++i)
564  {
565  ids[i] = p->m_vertexList[i]->m_id;
566  }
567 
568  std::sort(ids.begin(), ids.end());
569  boost::hash_range(seed, ids.begin(), ids.end());
570 
571  return seed;
572  }
573  };
574  typedef boost::unordered_set<FaceSharedPtr, FaceHash> FaceSet;
575 
576 
577  /**
578  * @brief Basic information about an element.
579  *
580  * ElmtConfig contains four member variables which denote the
581  * properties of an element when it is created.
582  */
583  struct ElmtConfig
584  {
585  ElmtConfig(LibUtilities::ShapeType pE, unsigned int pOrder,
586  bool pFn, bool pVn, bool pReorient = true,
589  m_e(pE), m_faceNodes(pFn), m_volumeNodes(pVn), m_order(pOrder),
590  m_reorient(pReorient), m_edgeCurveType(pECt), m_faceCurveType(pFCt) {}
591  ElmtConfig(ElmtConfig const &p) :
592  m_e(p.m_e), m_faceNodes(p.m_faceNodes), m_volumeNodes(p.m_volumeNodes),
593  m_order(p.m_order), m_reorient(p.m_reorient),
594  m_edgeCurveType(p.m_edgeCurveType), m_faceCurveType(p.m_faceCurveType) {}
595 
597 
598  /// Element type (e.g. triangle, quad, etc).
600  /// Denotes whether the element contains face nodes. For 2D
601  /// elements, if this is true then the element contains interior
602  /// nodes.
604  /// Denotes whether the element contains volume (i.e. interior)
605  /// nodes. These are not supported by either the mesh converter or
606  /// Nektar++ but are included for completeness and are required
607  /// for some output modules (e.g. Gmsh).
609  /// Order of the element.
610  unsigned int m_order;
611  /// Denotes whether the element needs to be re-orientated for a
612  /// spectral element framework.
614  /// Distribution of points in edges.
616  /// Distribution of points in faces.
618  };
619 
620 
621  /**
622  * @brief Base class for element definitions.
623  *
624  * An element is defined by a list of vertices, edges and faces
625  * (depending on the dimension of the problem). This base class
626  * provides the underlying structure.
627  */
628  class Element {
629  public:
630  Element(ElmtConfig pConf,
631  unsigned int pNumNodes,
632  unsigned int pGotNodes);
633 
634  /// Returns the ID of the element (or associated edge or face for
635  /// boundary elements).
636  unsigned int GetId() const {
637  if (m_faceLink.get() != 0) return m_faceLink->m_id;
638  if (m_edgeLink.get() != 0) return m_edgeLink->m_id;
639  return m_id;
640  }
641  /// Returns the expansion dimension of the element.
642  unsigned int GetDim() const {
643  return m_dim;
644  }
645  /// Returns the configuration of the element.
646  ElmtConfig GetConf() const {
647  return m_conf;
648  }
649  /// Returns the tag which defines the element shape.
650  std::string GetTag() const {
651  if (m_faceLink.get() != 0) return "F";
652  if (m_edgeLink.get() != 0) return "E";
653  return m_tag;
654  }
655  /// Access a vertex node.
656  NodeSharedPtr GetVertex(unsigned int i) const {
657  return m_vertex[i];
658  }
659  /// Access an edge.
660  EdgeSharedPtr GetEdge(unsigned int i) const {
661  return m_edge[i];
662  }
663  /// Access a face.
664  FaceSharedPtr GetFace(unsigned int i) const {
665  return m_face[i];
666  }
667  /// Access the list of vertex nodes.
668  std::vector<NodeSharedPtr> GetVertexList() const {
669  return m_vertex;
670  }
671  /// Access the list of edges.
672  std::vector<EdgeSharedPtr> GetEdgeList() const {
673  return m_edge;
674  }
675  /// Access the list of faces.
676  std::vector<FaceSharedPtr> GetFaceList() const {
677  return m_face;
678  }
679  /// Access the list of volume nodes.
680  std::vector<NodeSharedPtr> GetVolumeNodes() const {
681  return m_volumeNodes;
682  }
683  void SetVolumeNodes(std::vector<NodeSharedPtr> &nodes) {
684  m_volumeNodes = nodes;
685  }
687  return m_curveType;
688  }
690  m_curveType = cT;
691  }
692  /// Returns the total number of nodes (vertices, edge nodes and
693  /// face nodes and volume nodes).
694  unsigned int GetNodeCount() const
695  {
696  unsigned int n = m_volumeNodes.size();
697  if (m_dim == 2)
698  {
699  for (int i = 0; i < m_edge.size(); ++i)
700  {
701  n += m_edge[i]->GetNodeCount();
702  }
703  n -= m_vertex.size();
704  }
705  else
706  {
707  cerr << "Not supported." << endl;
708  exit(1);
709  }
710  return n;
711  }
712  /// Access the list of tags associated with this element.
713  std::vector<int> GetTagList() const {
714  return m_taglist;
715  }
716  /// Returns the number of vertices.
717  unsigned int GetVertexCount() const {
718  return m_vertex.size();
719  }
720  /// Returns the number of edges.
721  unsigned int GetEdgeCount() const {
722  return m_edge.size();
723  }
724  /// Returns the number of faces.
725  unsigned int GetFaceCount() const {
726  return m_face.size();
727  }
728  /// Change the ID of the element.
729  void SetId(unsigned int p) {
730  m_id = p;
731  }
732  /// Replace a vertex with another vertex object.
733  void SetVertex(unsigned int p, NodeSharedPtr pNew);
734  /// Replace an edge with another edge object.
735  void SetEdge(unsigned int p, EdgeSharedPtr pNew);
736  /// Replace a face with another face object.
737  void SetFace(unsigned int p, FaceSharedPtr pNew);
738  /// Set a correspondence between this element and an edge
739  /// (2D boundary element).
741  m_edgeLink = pLink;
742  }
743  /// Get correspondence between this element and an edge.
745  return m_edgeLink;
746  }
747  /// Set a correspondence between this element and a face
748  /// (3D boundary element).
750  m_faceLink = pLink;
751  }
752  /// Get correspondence between this element and a face.
754  return m_faceLink;
755  }
756  /// Set a correspondence between edge or face i and its
757  /// representative boundary element m->element[expDim-1][j].
758  void SetBoundaryLink(int i, int j) {
759  m_boundaryLinks[i] = j;
760  }
761  /// Get the location of the boundary face/edge i for this element.
762  int GetBoundaryLink(int i) {
763  std::map<int,int>::iterator it = m_boundaryLinks.find(i);
764  if (it == m_boundaryLinks.end())
765  {
766  return -1;
767  }
768  else
769  {
770  return it->second;
771  }
772  }
773  /// Set the list of tags associated with this element.
774  void SetTagList(const std::vector<int> &tags)
775  {
776  m_taglist = tags;
777  }
778  /// Generate a list of vertices (1D), edges (2D), or faces (3D).
779  virtual std::string GetXmlString() const
780  {
781  std::stringstream s;
782  switch (m_dim)
783  {
784  case 1:
785  for(int j=0; j< m_vertex.size(); ++j)
786  {
787  s << std::setw(5) << m_vertex[j]->m_id << " ";
788  }
789  break;
790  case 2:
791  for(int j=0; j< m_edge.size(); ++j)
792  {
793  s << std::setw(5) << m_edge[j]->m_id << " ";
794  }
795  break;
796  case 3:
797  for(int j=0; j< m_face.size(); ++j)
798  {
799  s << std::setw(5) << m_face[j]->m_id << " ";
800  }
801  break;
802  }
803  return s.str();
804  }
805 
806  /**
807  * @brief Reorders Gmsh ordered nodes into a row-by-row ordering
808  * required for Nektar++ curve tags.
809  *
810  * The interior nodes of elements in the Gmsh format are ordered
811  * as for a lower-order element of the same type. This promotes
812  * the recursive approach to the reordering algorithm.
813  */
814  std::vector<NodeSharedPtr> tensorNodeOrdering(
815  const std::vector<NodeSharedPtr> &nodes,
816  int n) const
817  {
818  std::vector<NodeSharedPtr> nodeList;
819  int cnt2;
820 
821  // Triangle
822  if (m_vertex.size() == 3)
823  {
824  nodeList.resize(nodes.size());
825 
826  // Vertices
827  nodeList[0] = nodes[0];
828  if (n > 1)
829  {
830  nodeList[n-1] = nodes[1];
831  nodeList[n*(n+1)/2 - 1] = nodes[2];
832  }
833 
834  // Edges
835  int cnt = n;
836  for (int i = 1; i < n-1; ++i)
837  {
838  nodeList[i] = nodes[3+i-1];
839  nodeList[cnt] = nodes[3+3*(n-2)-i];
840  nodeList[cnt+n-i-1] = nodes[3+(n-2)+i-1];
841  cnt += n-i;
842  }
843 
844  // Interior (recursion)
845  if (n > 3)
846  {
847  // Reorder interior nodes
848  std::vector<NodeSharedPtr> interior((n-3)*(n-2)/2);
849  std::copy(nodes.begin() + 3+3*(n-2), nodes.end(), interior.begin());
850  interior = tensorNodeOrdering(interior, n-3);
851 
852  // Copy into full node list
853  cnt = n;
854  cnt2 = 0;
855  for (int j = 1; j < n-2; ++j)
856  {
857  for (int i = 0; i < n-j-2; ++i)
858  {
859  nodeList[cnt+i+1] = interior[cnt2+i];
860  }
861  cnt += n-j;
862  cnt2 += n-2-j;
863  }
864  }
865  }
866  // Quad
867  else if (m_dim == 2 && m_vertex.size() == 4)
868  {
869  nodeList.resize(nodes.size());
870 
871  // Vertices and edges
872  nodeList[0] = nodes[0];
873  if (n > 1)
874  {
875  nodeList[n-1] = nodes[1];
876  nodeList[n*n-1] = nodes[2];
877  nodeList[n*(n-1)] = nodes[3];
878  }
879  for (int i = 1; i < n-1; ++i)
880  {
881  nodeList[i] = nodes[4+i-1];
882  }
883  for (int i = 1; i < n-1; ++i)
884  {
885  nodeList[n*n-1-i] = nodes[4+2*(n-2)+i-1];
886  }
887 
888  // Interior (recursion)
889  if (n > 2)
890  {
891  // Reorder interior nodes
892  std::vector<NodeSharedPtr> interior((n-2)*(n-2));
893  std::copy(nodes.begin() + 4+4*(n-2), nodes.end(), interior.begin());
894  interior = tensorNodeOrdering(interior, n-2);
895 
896  // Copy into full node list
897  for (int j = 1; j < n-1; ++j)
898  {
899  nodeList[j*n] = nodes[4+3*(n-2)+n-2-j];
900  for (int i = 1; i < n-1; ++i)
901  {
902  nodeList[j*n+i] = interior[(j-1)*(n-2)+(i-1)];
903  }
904  nodeList[(j+1)*n-1] = nodes[4+(n-2)+j-1];
905  }
906  }
907  }
908  else
909  {
910  cerr << "TensorNodeOrdering for a " << m_vertex.size()
911  << "-vertex element is not yet implemented." << endl;
912  }
913  return nodeList;
914  }
915 
916  /// Generates a string listing the coordinates of all nodes
917  /// associated with this element.
918  std::string GetXmlCurveString() const
919  {
920  // Temporary node list for reordering
921  std::vector<NodeSharedPtr> nodeList;
922 
923  // Node orderings are different for different elements.
924  // Triangle
925  if (m_vertex.size() == 3)
926  {
927  int n = m_edge[0]->GetNodeCount();
928  nodeList.resize(n*(n+1)/2);
929 
930  // Populate nodelist
931  std::copy(m_vertex.begin(), m_vertex.end(), nodeList.begin());
932  for (int i = 0; i < 3; ++i)
933  {
934  std::copy(m_edge[i]->m_edgeNodes.begin(), m_edge[i]->m_edgeNodes.end(), nodeList.begin() + 3 + i*(n-2));
935  if (m_edge[i]->m_n1 != m_vertex[i])
936  {
937  // If edge orientation is reversed relative to node
938  // ordering, we need to reverse order of nodes.
939  std::reverse(nodeList.begin() + 3 + i*(n-2),
940  nodeList.begin() + 3 + (i+1)*(n-2));
941  }
942  }
943 
944  // Triangle ordering lists vertices, edges then interior.
945  // Interior nodes are row by row from edge 0 up to vertex 2
946  // so need to reorder interior nodes only.
947  std::vector<NodeSharedPtr> interior(m_volumeNodes.size());
948  std::copy(m_volumeNodes.begin(), m_volumeNodes.end(), interior.begin());
949  interior = tensorNodeOrdering(interior, n-3);
950  std::copy(interior.begin(), interior.end(), nodeList.begin() + 3*(n-1));
951  }
952  // Quad
953  else if (m_dim == 2 && m_vertex.size() == 4)
954  {
955  int n = m_edge[0]->GetNodeCount();
956  nodeList.resize(n*n);
957 
958  // Populate nodelist
959  std::copy(m_vertex.begin(), m_vertex.end(), nodeList.begin());
960  for (int i = 0; i < 4; ++i)
961  {
962  std::copy(m_edge[i]->m_edgeNodes.begin(),
963  m_edge[i]->m_edgeNodes.end(),
964  nodeList.begin() + 4 + i*(n-2));
965 
966  if (m_edge[i]->m_n1 != m_vertex[i])
967  {
968  // If m_edge orientation is reversed relative to node
969  // ordering, we need to reverse order of nodes.
970  std::reverse(nodeList.begin() + 4 + i*(n-2),
971  nodeList.begin() + 4 + (i+1)*(n-2));
972  }
973  }
974  std::copy(m_volumeNodes.begin(), m_volumeNodes.end(), nodeList.begin() + 4*(n-1));
975 
976  // Quadrilateral ordering lists all nodes row by row
977  // starting from edge 0 up to edge 2, so need to reorder
978  // all nodes.
979  nodeList = tensorNodeOrdering(nodeList, n);
980  }
981  else
982  {
983  cerr << "GetXmlCurveString for a " << m_vertex.size()
984  << "-vertex element is not yet implemented." << endl;
985  }
986 
987  // Finally generate the XML string corresponding to our new
988  // node reordering.
989  std::stringstream s;
990  std::string str;
991  for (int k = 0; k < nodeList.size(); ++k)
992  {
993  s << std::scientific << std::setprecision(8) << " "
994  << nodeList[k]->m_x << " " << nodeList[k]->m_y
995  << " " << nodeList[k]->m_z << " ";
996 
997  }
998  return s.str();
999  }
1000 
1001  /// Generate a Nektar++ geometry object for this element.
1003  {
1004  ASSERTL0(false, "This function should be implemented at a shape level.");
1005  return boost::shared_ptr<SpatialDomains::Geometry>();
1006  }
1007  int GetMaxOrder();
1008  /// Complete this object.
1009  virtual void Complete(int order)
1010  {
1011  ASSERTL0(false, "This function should be implemented at a shape level.");
1012  }
1013  void Print()
1014  {
1015  int i, j;
1016  for (i = 0; i < m_vertex.size(); ++i)
1017  {
1018  cout << m_vertex[i]->m_x << " " << m_vertex[i]->m_y << " " << m_vertex[i]->m_z << endl;
1019  }
1020  for (i = 0; i < m_edge.size(); ++i)
1021  {
1022  for (j = 0; j < m_edge[i]->m_edgeNodes.size(); ++j)
1023  {
1024  NodeSharedPtr n = m_edge[i]->m_edgeNodes[j];
1025  cout << n->m_x << " " << n->m_y << " " << n->m_z << endl;
1026  }
1027  }
1028  for (i = 0; i < m_face.size(); ++i)
1029  {
1030  for (j = 0; j < m_face[i]->m_faceNodes.size(); ++j)
1031  {
1032  NodeSharedPtr n = m_face[i]->m_faceNodes[j];
1033  cout << n->m_x << " " << n->m_y << " " << n->m_z << endl;
1034  }
1035  }
1036  }
1037 
1038  protected:
1039  /// ID of the element.
1040  unsigned int m_id;
1041  /// Dimension of the element.
1042  unsigned int m_dim;
1043  /// Contains configuration of the element.
1045  /// Tag character describing the element.
1046  std::string m_tag;
1047  /// List of integers specifying properties of the element.
1048  std::vector<int> m_taglist;
1049  /// List of element vertex nodes.
1050  std::vector<NodeSharedPtr> m_vertex;
1051  /// List of element edges.
1052  std::vector<EdgeSharedPtr> m_edge;
1053  /// List of element faces.
1054  std::vector<FaceSharedPtr> m_face;
1055  /// List of element volume nodes.
1056  std::vector<NodeSharedPtr> m_volumeNodes;
1057  /// Volume curve type
1059  /// Pointer to the corresponding edge if element is a 2D boundary.
1061  /// Pointer to the corresponding face if element is a 3D boundary.
1063  /// Array mapping faces/edges to the location of the appropriate
1064  /// boundary elements in m->element.
1065  std::map<int,int> m_boundaryLinks;
1066  /// Nektar++ geometry object for this element.
1068  };
1069  /// Container for elements; key is expansion dimension, value is
1070  /// vector of elements of that dimension.
1071  typedef std::map<unsigned int, std::vector<ElementSharedPtr> > ElementMap;
1072  /// Element factory definition.
1074  ElmtConfig, std::vector<NodeSharedPtr>, std::vector<int> > ElementFactory;
1076 
1077  /// Define element ordering based on ID.
1079  {
1080  typedef boost::shared_ptr<Element> pT;
1081  bool operator()(const pT a, const pT b) const
1082  {
1083  // check for 0
1084  if (a.get() == 0)
1085  {
1086  // if b is also 0, then they are equal, hence a is not
1087  // less than b
1088  return b.get() != 0;
1089  }
1090  else if (b.get() == 0)
1091  {
1092  return false;
1093  }
1094  else
1095  {
1096  return a->GetId() < b->GetId();
1097  }
1098  }
1099  };
1100 
1101 
1102  /**
1103  * @brief A composite is a collection of elements.
1104  *
1105  * All elements should be of the same type, i.e. have the same tag.
1106  */
1107  class Composite {
1108  public:
1109  Composite() : m_reorder(true) {}
1110 
1111  /// Generate the list of IDs of elements within this composite.
1112  std::string GetXmlString(bool doSort=true);
1113 
1114  /// ID of composite.
1115  unsigned int m_id;
1116  /// Element type tag.
1117  std::string m_tag;
1118  /// Determines whether items can be reordered.
1120  /// List of elements in this composite.
1121  std::vector<ElementSharedPtr> m_items;
1122  };
1123  /// Shared pointer to a composite.
1124  typedef boost::shared_ptr<Composite> CompositeSharedPtr;
1125  /// Container of composites; key is the composite id, value is the
1126  /// composite.
1127  typedef std::map<unsigned int, CompositeSharedPtr> CompositeMap;
1128 
1129  /**
1130  * Enumeration of condition types (Dirichlet, Neumann, etc).
1131  */
1133  {
1140  };
1141 
1142  /**
1143  * @brief Defines a boundary condition.
1144  *
1145  * A boundary condition is defined by its type (e.g. Dirichlet), the
1146  * field it applies to, the value imposed on this field and the
1147  * composite which the boundary condition is applied to.
1148  */
1149  struct Condition
1150  {
1151  Condition() : type(), field(), value(), m_composite() {}
1152  std::vector<ConditionType> type;
1153  std::vector<std::string> field;
1154  std::vector<std::string> value;
1155  std::vector<int> m_composite;
1156  };
1157 
1158  typedef boost::shared_ptr<Condition> ConditionSharedPtr;
1159  typedef std::map<int,ConditionSharedPtr> ConditionMap;
1160 
1161  bool operator==(ConditionSharedPtr const &c1, ConditionSharedPtr const &c2);
1162 
1163  class Mesh
1164  {
1165  public:
1166  Mesh() : m_verbose(false) {}
1167 
1168  /// Verbose flag
1170  /// Dimension of the expansion.
1171  unsigned int m_expDim;
1172  /// Dimension of the space in which the mesh is defined.
1173  unsigned int m_spaceDim;
1174  /// List of mesh nodes.
1175  std::vector<NodeSharedPtr> m_node;
1176  /// Set of element vertices.
1178  /// Set of element edges.
1180  /// Set of element faces.
1182  /// Map for elements.
1184  /// Map for composites.
1186  /// Boundary conditions maps tag to condition.
1188  /// List of fields names.
1189  std::vector<std::string> m_fields;
1190  /// Map of vertex normals.
1191  boost::unordered_map<int, Node> m_vertexNormals;
1192  /// Set of all pairs of element ID and edge/face number on which to
1193  /// apply spherigon surface smoothing.
1194  set<pair<int,int> > m_spherigonSurfs;
1195  /// Returns the total number of elements in the mesh with
1196  /// dimension expDim.
1197  unsigned int GetNumElements();
1198  /// Returns the total number of elements in the mesh with
1199  /// dimension < expDim.
1200  unsigned int GetNumBndryElements();
1201  /// Returns the total number of entities in the mesh.
1202  unsigned int GetNumEntities();
1203  };
1204  /// Shared pointer to a mesh.
1205  typedef boost::shared_ptr<Mesh> MeshSharedPtr;
1206 
1207 
1208  /**
1209  * @brief A 0-dimensional vertex.
1210  */
1211  class Point : public Element {
1212  public:
1213  /// Creates an instance of this class
1214  static ElementSharedPtr create(
1215  ElmtConfig pConf,
1216  std::vector<NodeSharedPtr> pNodeList,
1217  std::vector<int> pTagList)
1218  {
1219  return boost::shared_ptr<Element>(
1220  new Point(pConf, pNodeList, pTagList));
1221  }
1222  /// Element type
1224 
1225  Point(ElmtConfig pConf,
1226  std::vector<NodeSharedPtr> pNodeList,
1227  std::vector<int> pTagList);
1228  Point(const Point& pSrc);
1229  virtual ~Point() {}
1230 
1231  static unsigned int GetNumNodes(ElmtConfig pConf);
1232  };
1233 
1234 
1235  /**
1236  * @brief A 1-dimensional line between two vertex nodes.
1237  */
1238  class Line : public Element {
1239  public:
1240  /// Creates an instance of this class
1241  static ElementSharedPtr create(
1242  ElmtConfig pConf,
1243  std::vector<NodeSharedPtr> pNodeList,
1244  std::vector<int> pTagList)
1245  {
1246  return boost::shared_ptr<Element>(
1247  new Line(pConf, pNodeList, pTagList));
1248  }
1249  /// Element type
1251 
1252  Line(ElmtConfig pConf,
1253  std::vector<NodeSharedPtr> pNodeList,
1254  std::vector<int> pTagList);
1255  Line(const Point& pSrc);
1256  virtual ~Line() {}
1257 
1258  virtual SpatialDomains::GeometrySharedPtr GetGeom(int coordDim);
1259 
1260  static unsigned int GetNumNodes(ElmtConfig pConf);
1261  };
1262 
1263 
1264  /**
1265  * @brief A 2-dimensional three-sided element.
1266  */
1267  class Triangle : public Element {
1268  public:
1269  /// Creates an instance of this class
1270  static ElementSharedPtr create(
1271  ElmtConfig pConf,
1272  std::vector<NodeSharedPtr> pNodeList,
1273  std::vector<int> pTagList)
1274  {
1275  ElementSharedPtr e = boost::shared_ptr<Element>(
1276  new Triangle(pConf, pNodeList, pTagList));
1277  vector<EdgeSharedPtr> m_edges = e->GetEdgeList();
1278  for (int i = 0; i < m_edges.size(); ++i)
1279  {
1280  m_edges[i]->m_elLink.push_back(pair<ElementSharedPtr, int>(e,i));
1281  }
1282  return e;
1283  }
1284  /// Element type
1286 
1287  Triangle(ElmtConfig pConf,
1288  std::vector<NodeSharedPtr> pNodeList,
1289  std::vector<int> pTagList);
1290  Triangle(const Triangle& pSrc);
1291  virtual ~Triangle() {}
1292 
1293  virtual SpatialDomains::GeometrySharedPtr GetGeom(int coordDim);
1294  virtual void Complete(int order);
1295 
1296  static unsigned int GetNumNodes(ElmtConfig pConf);
1297  };
1298 
1299 
1300  /**
1301  * @brief A 2-dimensional four-sided element.
1302  */
1303  class Quadrilateral : public Element {
1304  public:
1305  /// Creates an instance of this class
1306  static ElementSharedPtr create(
1307  ElmtConfig pConf,
1308  std::vector<NodeSharedPtr> pNodeList,
1309  std::vector<int> pTagList)
1310  {
1311  ElementSharedPtr e = boost::shared_ptr<Element>(
1312  new Quadrilateral(pConf, pNodeList, pTagList));
1313  vector<EdgeSharedPtr> m_edges = e->GetEdgeList();
1314  for (int i = 0; i < m_edges.size(); ++i)
1315  {
1316  m_edges[i]->m_elLink.push_back(pair<ElementSharedPtr, int>(e,i));
1317  }
1318  return e;
1319  }
1320  /// Element type
1322 
1323  Quadrilateral(ElmtConfig pConf,
1324  std::vector<NodeSharedPtr> pNodeList,
1325  std::vector<int> pTagList);
1326  Quadrilateral(const Quadrilateral& pSrc);
1327  virtual ~Quadrilateral() {}
1328 
1329  virtual SpatialDomains::GeometrySharedPtr GetGeom(int coordDim);
1330  virtual void Complete(int order);
1331 
1332  static unsigned int GetNumNodes(ElmtConfig pConf);
1333  };
1334 
1335 
1336  /**
1337  * @brief A 3-dimensional four-faced element.
1338  */
1339  class Tetrahedron : public Element {
1340  public:
1341  /// Creates an instance of this class
1342  static ElementSharedPtr create(
1343  ElmtConfig pConf,
1344  std::vector<NodeSharedPtr> pNodeList,
1345  std::vector<int> pTagList)
1346  {
1347  ElementSharedPtr e = boost::shared_ptr<Element>(
1348  new Tetrahedron(pConf, pNodeList, pTagList));
1349  vector<FaceSharedPtr> faces = e->GetFaceList();
1350  for (int i = 0; i < faces.size(); ++i)
1351  {
1352  faces[i]->m_elLink.push_back(pair<ElementSharedPtr, int>(e,i));
1353  }
1354  return e;
1355  }
1356  /// Element type
1358 
1359  Tetrahedron(ElmtConfig pConf,
1360  std::vector<NodeSharedPtr> pNodeList,
1361  std::vector<int> pTagList);
1362  Tetrahedron(const Tetrahedron& pSrc);
1363  virtual ~Tetrahedron() {}
1364 
1365  virtual SpatialDomains::GeometrySharedPtr GetGeom(int coordDim);
1366  virtual void Complete(int order);
1367 
1368  static unsigned int GetNumNodes(ElmtConfig pConf);
1369 
1370  /**
1371  * Orientation of tet; unchanged = 0; base vertex swapped = 1.
1372  */
1373  int m_orientationMap[4];
1374 
1375  protected:
1376  void OrientTet();
1377  };
1378 
1379 
1380  /**
1381  * @brief A 3-dimensional square-based pyramidic element
1382  */
1383  class Pyramid : public Element {
1384  public:
1385  /// Creates an instance of this class
1386  static ElementSharedPtr create(
1387  ElmtConfig pConf,
1388  std::vector<NodeSharedPtr> pNodeList,
1389  std::vector<int> pTagList)
1390  {
1391  ElementSharedPtr e = boost::shared_ptr<Element>(
1392  new Pyramid(pConf, pNodeList, pTagList));
1393  vector<FaceSharedPtr> faces = e->GetFaceList();
1394  for (int i = 0; i < faces.size(); ++i)
1395  {
1396  faces[i]->m_elLink.push_back(pair<ElementSharedPtr, int>(e,i));
1397  }
1398  return e;
1399  }
1400  /// Element type
1402 
1403  Pyramid(ElmtConfig pConf,
1404  std::vector<NodeSharedPtr> pNodeList,
1405  std::vector<int> pTagList);
1406  Pyramid(const Pyramid& pSrc);
1407  virtual ~Pyramid() {}
1408 
1409  virtual SpatialDomains::GeometrySharedPtr GetGeom(int coordDim);
1410  static unsigned int GetNumNodes(ElmtConfig pConf);
1411 
1412  /**
1413  * Orientation of pyramid.
1414  */
1415  int orientationMap[5];
1416  };
1417 
1418  /**
1419  * @brief A 3-dimensional five-faced element (2 triangles, 3
1420  * quadrilaterals).
1421  */
1422  class Prism : public Element {
1423  public:
1424  /// Creates an instance of this class
1425  static ElementSharedPtr create(
1426  ElmtConfig pConf,
1427  std::vector<NodeSharedPtr> pNodeList,
1428  std::vector<int> pTagList)
1429  {
1430  ElementSharedPtr e = boost::shared_ptr<Element>(
1431  new Prism(pConf, pNodeList, pTagList));
1432  vector<FaceSharedPtr> faces = e->GetFaceList();
1433  for (int i = 0; i < faces.size(); ++i)
1434  {
1435  faces[i]->m_elLink.push_back(pair<ElementSharedPtr, int>(e,i));
1436  }
1437  return e;
1438  }
1439  /// Element type
1441 
1442  Prism(ElmtConfig pConf,
1443  std::vector<NodeSharedPtr> pNodeList,
1444  std::vector<int> pTagList);
1445  Prism(const Prism& pSrc);
1446  virtual ~Prism() {}
1447 
1448  virtual SpatialDomains::GeometrySharedPtr GetGeom(int coordDim);
1449  virtual void Complete(int order);
1450 
1451  static unsigned int GetNumNodes(ElmtConfig pConf);
1452 
1453  /**
1454  * Orientation of prism; unchanged = 0; clockwise = 1;
1455  * counter-clockwise = 2. This is set by OrientPrism.
1456  */
1457  unsigned int m_orientation;
1458 
1459  protected:
1460  void OrientPrism();
1461  };
1462 
1463 
1464  /**
1465  * @brief A 3-dimensional six-faced element.
1466  */
1467  class Hexahedron : public Element {
1468  public:
1469  /// Creates an instance of this class
1470  static ElementSharedPtr create(
1471  ElmtConfig pConf,
1472  std::vector<NodeSharedPtr> pNodeList,
1473  std::vector<int> pTagList)
1474  {
1475  ElementSharedPtr e = boost::shared_ptr<Element>(
1476  new Hexahedron(pConf, pNodeList, pTagList));
1477  vector<FaceSharedPtr> faces = e->GetFaceList();
1478  for (int i = 0; i < faces.size(); ++i)
1479  {
1480  faces[i]->m_elLink.push_back(pair<ElementSharedPtr, int>(e,i));
1481  }
1482  return e;
1483  }
1484  /// Element type
1486 
1487  Hexahedron(ElmtConfig pConf,
1488  std::vector<NodeSharedPtr> pNodeList,
1489  std::vector<int> pTagList);
1490  Hexahedron(const Hexahedron& pSrc);
1491  virtual ~Hexahedron() {}
1492 
1493  virtual SpatialDomains::GeometrySharedPtr GetGeom(int coordDim);
1494 
1495  static unsigned int GetNumNodes(ElmtConfig pConf);
1496  };
1497  }
1498 }
1499 
1500 #endif