Intrepid2
Intrepid2_TensorTopologyMap.hpp
Go to the documentation of this file.
1 // @HEADER
2 // *****************************************************************************
3 // Intrepid2 Package
4 //
5 // Copyright 2007 NTESS and the Intrepid2 contributors.
6 // SPDX-License-Identifier: BSD-3-Clause
7 // *****************************************************************************
8 // @HEADER
9 
15 #ifndef Intrepid2_TensorTopologyMap_h
16 #define Intrepid2_TensorTopologyMap_h
17 
18 #include "Intrepid2_Utils.hpp"
19 
20 #include "Shards_CellTopology.hpp"
21 
22 #include <map>
23 #include <set>
24 #include <vector>
25 
26 namespace Intrepid2
27 {
37  {
38  shards::CellTopology cellTopo1_;
39  shards::CellTopology cellTopo2_;
40  shards::CellTopology compositeCellTopo_;
41 
42  using Subcell = std::pair<unsigned,unsigned>; // first: dimension, second: subcell ordinal
43  using SubcellPair = std::pair<Subcell,Subcell>; // first: subcell in cellTopo1_, second: subcell in cellTopo2_
44  std::map<SubcellPair,Subcell> subcellMap_; // values are the subcell in compositeCellTopo (the dimension of this subcell is the sum of the dimensions in the SubcellPair)
45  public:
60  TensorTopologyMap(shards::CellTopology cellTopo1, shards::CellTopology cellTopo2, shards::CellTopology compositeCellTopo,
61  std::vector< std::pair<unsigned,unsigned> > nodePairs = std::vector< std::pair<unsigned,unsigned> >())
62  :
63  cellTopo1_(cellTopo1),
64  cellTopo2_(cellTopo2),
65  compositeCellTopo_(compositeCellTopo)
66  {
67  if (nodePairs.size() == 0)
68  {
69  nodePairs = defaultNodePairs(cellTopo1, cellTopo2, compositeCellTopo);
70  }
71 
72  auto spaceDim1 = cellTopo1.getDimension();
73  auto spaceDim2 = cellTopo2.getDimension();
74  auto spaceDim = compositeCellTopo.getDimension();
75  INTREPID2_TEST_FOR_EXCEPTION(spaceDim1 + spaceDim2 != spaceDim, std::invalid_argument, "incompatible spatial dimensions");
76  std::map<std::pair<unsigned,unsigned>,unsigned> compositeNodeOrdinalMap;
77  for (unsigned compositeNodeOrdinal=0; compositeNodeOrdinal<nodePairs.size(); compositeNodeOrdinal++)
78  {
79  compositeNodeOrdinalMap[nodePairs[compositeNodeOrdinal]] = compositeNodeOrdinal;
80  }
81  for (unsigned d1=0; d1<=spaceDim1; d1++)
82  {
83  unsigned subcellCount1 = cellTopo1.getSubcellCount(d1);
84  for (unsigned subcellOrdinal1=0; subcellOrdinal1<subcellCount1; subcellOrdinal1++)
85  {
86  Subcell subcell1 = {d1, subcellOrdinal1};
87  std::set<unsigned> subcell1Nodes; // set because we don't care about ordering
88  unsigned nodeCount1 = cellTopo1_.getNodeCount(d1, subcellOrdinal1);
89  // unfortunately, the node count for vertices is given as 0 by shards::CellTopology. This seems like a bug.
90  if (d1 == 0)
91  {
92  subcell1Nodes.insert(subcellOrdinal1);
93  }
94  else
95  {
96  for (unsigned nodeOrdinal1=0; nodeOrdinal1<nodeCount1; nodeOrdinal1++)
97  {
98  subcell1Nodes.insert(cellTopo1_.getNodeMap(d1, subcellOrdinal1, nodeOrdinal1));
99  }
100  }
101  for (unsigned d2=0; d2<=spaceDim2; d2++)
102  {
103  unsigned subcellCount2 = cellTopo2.getSubcellCount(d2);
104  for (unsigned subcellOrdinal2=0; subcellOrdinal2<subcellCount2; subcellOrdinal2++)
105  {
106  Subcell subcell2 = {d2, subcellOrdinal2};
107  std::set<unsigned> subcell2Nodes; // set because we don't care about ordering
108  unsigned nodeCount2 = cellTopo2_.getNodeCount(d2, subcellOrdinal2);
109  // unfortunately, the node count for vertices is given as 0 by shards::CellTopology. This seems like a bug.
110  if (d2 == 0)
111  {
112  subcell2Nodes.insert(subcellOrdinal2);
113  }
114  else
115  {
116  for (unsigned nodeOrdinal2=0; nodeOrdinal2<nodeCount2; nodeOrdinal2++)
117  {
118  subcell2Nodes.insert(cellTopo2_.getNodeMap(d2, subcellOrdinal2, nodeOrdinal2));
119  }
120  }
121 
122  std::set<unsigned> compositeNodes; // all the nodes from subcell1 times nodes from subcell2
123  for (auto subcellNode1 : subcell1Nodes)
124  {
125  for (auto subcellNode2 : subcell2Nodes)
126  {
127  INTREPID2_TEST_FOR_EXCEPTION(compositeNodeOrdinalMap.find({subcellNode1,subcellNode2}) == compositeNodeOrdinalMap.end(),
128  std::invalid_argument, "Node combination not found in map");
129  compositeNodes.insert(compositeNodeOrdinalMap[{subcellNode1,subcellNode2}]);
130  }
131  }
132  // now, search the composite topology for the unique subcell that involves all those nodes
133  // we do a brute-force search; we'll never have very big topologies, so the cost is not an issue
134  unsigned compositeSubcellDim = d1 + d2;
135  unsigned compositeSubcellCount = compositeCellTopo.getSubcellCount(compositeSubcellDim);
136  bool compositeSubcellFound = false;
137  for (unsigned compositeSubcellOrdinal=0; compositeSubcellOrdinal<compositeSubcellCount; compositeSubcellOrdinal++)
138  {
139  unsigned compositeSubcellNodeCount = (compositeSubcellDim > 0) ? compositeCellTopo.getNodeCount(compositeSubcellDim, compositeSubcellOrdinal)
140  : 1; // again, dealing with the fact that the node count for vertices is defined as 0, not 1
141  if (compositeSubcellNodeCount != compositeNodes.size()) continue; // node counts don't match, so this is not the subcell we're looking for
142  bool matches = true; // if we don't find a node that isn't contained in compositeNodes, then this is the subcell we're looking for
143  for (unsigned compositeSubcellNode=0; compositeSubcellNode<compositeSubcellNodeCount; compositeSubcellNode++)
144  {
145  unsigned nodeInCell = compositeCellTopo.getNodeMap(compositeSubcellDim, compositeSubcellOrdinal, compositeSubcellNode);
146  if (compositeNodes.find(nodeInCell) == compositeNodes.end())
147  {
148  matches = false;
149  break;
150  }
151  }
152  if (matches)
153  {
154  compositeSubcellFound = true;
155  subcellMap_[{subcell1,subcell2}] = {compositeSubcellDim, compositeSubcellOrdinal};
156  break;
157  }
158  }
159  INTREPID2_TEST_FOR_EXCEPTION(!compositeSubcellFound, std::invalid_argument, "Composite subcell not found");
160  }
161  }
162  }
163  }
164  }
165  TensorTopologyMap(shards::CellTopology cellTopo1, shards::CellTopology cellTopo2)
166  :
167  TensorTopologyMap(cellTopo1, cellTopo2, compositeCellTopology(cellTopo1,cellTopo2)) {}
168 
180  unsigned getCompositeSubcellOrdinal(unsigned subcell1Dim, unsigned subcell1Ordinal, unsigned subcell2Dim, unsigned subcell2Ordinal)
181  {
182  Subcell subcell1 = {subcell1Dim, subcell1Ordinal};
183  Subcell subcell2 = {subcell2Dim, subcell2Ordinal};
184  auto entry = subcellMap_.find({subcell1,subcell2});
185  INTREPID2_TEST_FOR_EXCEPTION(entry == subcellMap_.end(), std::invalid_argument, "entry not found");
186  auto subcell = entry->second;
187  return subcell.second; // subcell ordinal
188  }
189 
202  static std::vector< std::vector<int> > referenceCellGeometry(shards::CellTopology cellTopo)
203  {
204  std::vector< std::vector<int> > nodes(cellTopo.getVertexCount());
205  auto key = cellTopo.getKey();
206  switch (key)
207  {
208  case shards::Node::key:
209  nodes.push_back({}); // node.getVertexCount() returns 0; we do want a single (empty) entry, though, even though there's no spatial variation
210  break;
211  case shards::Line<2>::key:
212  nodes[0] = {0};
213  nodes[1] = {1};
214  break;
215  case shards::Triangle<3>::key:
216  nodes[0] = {0,0};
217  nodes[1] = {1,0};
218  nodes[2] = {0,1};
219  break;
220  case shards::Quadrilateral<4>::key:
221  nodes[0] = {0,0};
222  nodes[1] = {1,0};
223  nodes[2] = {1,1};
224  nodes[3] = {0,1};
225  break;
226  case shards::Hexahedron<8>::key:
227  nodes[0] = {0,0,0};
228  nodes[1] = {1,0,0};
229  nodes[2] = {1,1,0};
230  nodes[3] = {0,1,0};
231  nodes[4] = {0,0,1};
232  nodes[5] = {1,0,1};
233  nodes[6] = {1,1,1};
234  nodes[7] = {0,1,1};
235  break;
236  case shards::Wedge<6>::key: // wedge is triangle in (x,y) times line in z
237  nodes[0] = {0,0,0};
238  nodes[1] = {1,0,0};
239  nodes[2] = {0,1,0};
240  nodes[3] = {0,0,1};
241  nodes[4] = {1,0,1};
242  nodes[5] = {0,1,1};
243  break;
244  default:
245  INTREPID2_TEST_FOR_EXCEPTION(true, std::invalid_argument, "Unsupported CellTopology");
246  }
247  return nodes;
248  }
249 
263  static std::vector< std::pair<unsigned,unsigned> > defaultNodePairs(shards::CellTopology cellTopo1, shards::CellTopology cellTopo2, shards::CellTopology compositeCellTopo)
264  {
265  unsigned compositeNodeCount = compositeCellTopo.getVertexCount();
266  unsigned nodeCount1 = cellTopo1.getVertexCount();
267  unsigned nodeCount2 = cellTopo2.getVertexCount();
268  INTREPID2_TEST_FOR_EXCEPTION(compositeNodeCount != nodeCount1 * nodeCount2, std::invalid_argument, "Incompatible topologies");
269  std::vector< std::pair<unsigned,unsigned> > nodePairs(compositeNodeCount);
270  auto nodes1 = referenceCellGeometry(cellTopo1);
271  auto nodes2 = referenceCellGeometry(cellTopo2);
272  auto compositeNodes = referenceCellGeometry(compositeCellTopo);
273  std::map< std::vector<int>, unsigned > compositeNodeMap;
274  for (unsigned compositeNodeOrdinal=0; compositeNodeOrdinal<compositeNodeCount; compositeNodeOrdinal++)
275  {
276  compositeNodeMap[compositeNodes[compositeNodeOrdinal]] = compositeNodeOrdinal;
277  }
278  for (unsigned nodeOrdinal1=0; nodeOrdinal1<nodeCount1; nodeOrdinal1++)
279  {
280  std::vector<int> node1 = nodes1[nodeOrdinal1];
281  for (unsigned nodeOrdinal2=0; nodeOrdinal2<nodeCount2; nodeOrdinal2++)
282  {
283  std::vector<int> node2 = nodes2[nodeOrdinal2];
284  std::vector<int> compositeNode(node1); // copy node1 into the first slots of compositeNode
285  compositeNode.insert(compositeNode.end(), node2.begin(), node2.end());
286  auto compositeNodeMapEntry = compositeNodeMap.find(compositeNode);
287  INTREPID2_TEST_FOR_EXCEPTION(compositeNodeMapEntry == compositeNodeMap.end(), std::invalid_argument, "composite node not found in map");
288  nodePairs[compositeNodeMapEntry->second] = {nodeOrdinal1,nodeOrdinal2};
289  }
290  }
291  return nodePairs;
292  }
293 
301  static shards::CellTopology compositeCellTopology(shards::CellTopology cellTopo1, shards::CellTopology cellTopo2)
302  {
303  if (cellTopo1.getBaseKey() == shards::Node::key)
304  {
305  return cellTopo2;
306  }
307  else if (cellTopo2.getBaseKey() == shards::Node::key)
308  {
309  return cellTopo1;
310  }
311 
312  // if we get here, neither is a Node
313  auto key1 = cellTopo1.getBaseKey();
314  auto key2 = cellTopo2.getBaseKey();
315  switch (key1)
316  {
317  case shards::Line<2>::key:
318  switch (key2)
319  {
320  case shards::Line<2>::key:
321  return shards::CellTopology(shards::getCellTopologyData<shards::Quadrilateral<> >() );
322  case shards::Triangle<3>::key:
323  // unsupported:
324  INTREPID2_TEST_FOR_EXCEPTION(true,std::invalid_argument,"line x triangle is not supported at present. Wedge is triangle x line.");
325  case shards::Quadrilateral<4>::key:
326  return shards::CellTopology(shards::getCellTopologyData<shards::Hexahedron<> >() );
327  default:
328  INTREPID2_TEST_FOR_EXCEPTION(true,std::invalid_argument,"Unsupported cell topology pair");
329  }
330  case shards::Triangle<3>::key:
331  switch (key2)
332  {
333  case shards::Line<2>::key:
334  return shards::CellTopology(shards::getCellTopologyData<shards::Wedge<> >() );
335  default:
336  INTREPID2_TEST_FOR_EXCEPTION(true,std::invalid_argument,"Unsupported cell topology pair");
337  }
338  case shards::Quadrilateral<4>::key:
339  switch (key2)
340  {
341  case shards::Line<2>::key:
342  return shards::CellTopology(shards::getCellTopologyData<shards::Hexahedron<> >() );
343  default:
344  INTREPID2_TEST_FOR_EXCEPTION(true,std::invalid_argument,"Unsupported cell topology pair");
345  }
346  default:
347  INTREPID2_TEST_FOR_EXCEPTION(true,std::invalid_argument,"Unsupported cell topology pair");
348  }
349  }
350  };
351 } // end namespace Intrepid2
352 
353 #endif /* Intrepid2_TensorTopology_h */
static std::vector< std::vector< int > > referenceCellGeometry(shards::CellTopology cellTopo)
A topological representation of the vertex geometries corresponding to a cell topology.
static shards::CellTopology compositeCellTopology(shards::CellTopology cellTopo1, shards::CellTopology cellTopo2)
The composite cell topology generated as the tensor product of the specified component cell topologie...
For two cell topologies whose tensor product is a third, this class establishes a mapping from subcel...
Header function for Intrepid2::Util class and other utility functions.
static std::vector< std::pair< unsigned, unsigned > > defaultNodePairs(shards::CellTopology cellTopo1, shards::CellTopology cellTopo2, shards::CellTopology compositeCellTopo)
Default node pairs corresponding to the provided component and composite cell topologies.
unsigned getCompositeSubcellOrdinal(unsigned subcell1Dim, unsigned subcell1Ordinal, unsigned subcell2Dim, unsigned subcell2Ordinal)
Map from component subcell ordinals to the corresponding composite subcell ordinal.
TensorTopologyMap(shards::CellTopology cellTopo1, shards::CellTopology cellTopo2, shards::CellTopology compositeCellTopo, std::vector< std::pair< unsigned, unsigned > > nodePairs=std::vector< std::pair< unsigned, unsigned > >())
Constructor.