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
26namespace 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 */
Header function for Intrepid2::Util class and other utility functions.
For two cell topologies whose tensor product is a third, this class establishes a mapping from subcel...
unsigned getCompositeSubcellOrdinal(unsigned subcell1Dim, unsigned subcell1Ordinal, unsigned subcell2Dim, unsigned subcell2Ordinal)
Map from component subcell ordinals to the corresponding composite subcell ordinal.
static std::vector< std::vector< int > > referenceCellGeometry(shards::CellTopology cellTopo)
A topological representation of the vertex geometries corresponding to a cell topology.
TensorTopologyMap(shards::CellTopology cellTopo1, shards::CellTopology cellTopo2, shards::CellTopology compositeCellTopo, std::vector< std::pair< unsigned, unsigned > > nodePairs=std::vector< std::pair< unsigned, unsigned > >())
Constructor.
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...
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.