IOSS 2.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
Iotm_TextMeshAdjacencyGraph.h
Go to the documentation of this file.
1// Copyright(C) 1999-2020, 2022, 2023 National Technology & Engineering Solutions
2// of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with
3// NTESS, the U.S. Government retains certain rights in this software.
4
5#pragma once
6
7// ####################### Start Clang Header Tool Managed Headers ########################
8// clang-format off
9#include <ctype.h> // for toupper
10#include <stddef.h> // for size_t
11#include <algorithm> // for remove, etc
12#include <iterator> // for insert_iterator
13#include <map>
14#include <set> // for set
15#include <sstream> // for operator<<, etc
16#include <string> // for basic_string, etc
17#include <utility> // for pair
18#include <vector> // for vector
19#include <unordered_map>
20#include <sstream> // for ostringstream
21#include <iostream>
22#include <functional>
23#include <stdexcept>
24#include <numeric>
25#if defined(_WIN32) && !defined(__MINGW32__)
26#include <string.h>
27#define strcasecmp _stricmp
28#define strncasecmp _strnicmp
29#else
30#include <strings.h>
31#endif
32
33#include "Iotm_TextMeshFuncs.h"
35
36// clang-format on
37// ####################### End Clang Header Tool Managed Headers ########################
38namespace Iotm {
39 namespace text_mesh {
40
41 using ErrorHandler = std::function<void(const std::ostringstream &)>;
42
43 template <typename EntityId, typename Topology> struct TextMeshData;
44
45 template <typename EntityId, typename Topology> struct ElementData;
46
47 // Side (or face-face) adjacency is a means to compute neighbors of elements based on
48 // node connectivity of the sides bordering neighboring elements. The provided elements
49 // are defined in the [ std::vector<ElementData<EntityId, Topology>> elementDataVec ]
50 // member of the [ TextMeshData<EntityId, Topology> ] object and since neighbor connectivity
51 // based on element id will involve a search on this object, this implementation is based on
52 // indexing into this object for performance. Indices are zero-based and sides are one-based
53 //
54 // The adjacency graph may be created with element filtering based on proc and/or a list of
55 // selected element blocks. Any element which is selected by the filter (or all elements)
56 // is used as a basis to compute the list of filter nodes which is then used to compute a
57 // final list of "local" and "aura" elements which have any connection to these filter nodes.
58 // A map is also constructed for the list of these connected elements per filter node
59 //
60 // For every element in this final list, we can get the list of nodes of per side (based on
61 // topology) and using an intersection, find the list of common elements that share all the
62 // nodes on that side. These are the neighboring elements and we must determine how to connect
63 // the neighboring elements based on solid/shell connectivity rules. The basic rule is that
64 // shells act as "blockers" to solid-solid connections
65 //
66 // Polarity of faces is one determining factor and this is defined based on the relative
67 // permutation of the nodes on both faces under consideration
68 // +ve/+ve or -ve/-ve => same polarity
69 // -ve/+ve or +ve/-ve => opposite polarity
70 //
71 // {this face / that face}
72 // -----------------------
73 // Solid/solid connection:
74 // -> always allowed unless there is a shell already connected to either face or both
75 // faces have the same polarity (partially connected solids not allowed)
76 // Solid/shell connection:
77 // -> only allowed if both faces are opposite polarity. If the solid face is already
78 // connected
79 // to another solid, break the existing connection to insert the shell connection
80 // Shell/solid connection:
81 // -> only allowed if both faces are opposite polarity. If the solid face is already
82 // connected
83 // to another solid, break the existing connection to insert the shell connection
84 // Shell/shell connection:
85 // -> only allowed if both faces are opposite polarity AND neither shell face is already
86 // connected to another shell face
87 //
88 // Stacked shells on a solid element must also obey the rule that all the shells must live on
89 // the same processor. This is enforced as it is an error and unsupported in STK
90 //
91 template <typename EntityId, typename Topology> class SideAdjacencyGraph
92 {
93 public:
94 using IndexType = int64_t;
95
96 static constexpr int ANY_PROC = -1;
97 static constexpr int INVALID_SIDE = -1;
98 static constexpr IndexType INVALID_INDEX = -1;
99
101 {
104
105 FaceConnection(int thisSide_, IndexType otherElement_, int otherSide_)
106 : thisSide(thisSide_), thatElement(otherElement_), thatSide(otherSide_)
107 {
108 }
109
110 bool operator==(const FaceConnection &rhs) const
111 {
112 return ((thisSide == rhs.thisSide) && (thatElement == rhs.thatElement) &&
113 (thatSide == rhs.thatSide));
114 }
115
116 bool operator!=(const FaceConnection &rhs) const
117 {
118 return ((thisSide != rhs.thisSide) || (thatElement != rhs.thatElement) ||
119 (thatSide != rhs.thatSide));
120 }
121
122 bool operator<(const FaceConnection &rhs) const
123 {
124 if (thisSide < rhs.thisSide)
125 return true;
126 else if (thisSide == rhs.thisSide && thatElement < rhs.thatElement)
127 return true;
128 else if (thisSide == rhs.thisSide && thatElement == rhs.thatElement &&
129 thatSide < rhs.thatSide)
130 return true;
131 else
132 return false;
133 }
134
138 };
139
141 {
143 explicit FaceConnections(unsigned numSides_)
144 : numSides(numSides_), sideReference(numSides_, 0)
145 {
146 }
147
148 bool has_any_connection(int thisSide) const
149 {
150 FaceConnection minConnection(thisSide, INVALID_INDEX, 0);
151 typename std::set<FaceConnection>::iterator lowerBound =
152 std::lower_bound(connections.begin(), connections.end(), minConnection);
153
154 return (lowerBound != connections.end() && lowerBound->thisSide == thisSide);
155 }
156
157 bool has_any_connection(int thisSide, IndexType thatElem) const
158 {
159 FaceConnection minConnection(thisSide, thatElem, 0);
160 typename std::set<FaceConnection>::iterator lowerBound =
161 std::lower_bound(connections.begin(), connections.end(), minConnection);
162
163 return (lowerBound != connections.end() && lowerBound->thisSide == thisSide &&
164 lowerBound->thatElement == thatElem);
165 }
166
167 void add(const FaceConnection &connection)
168 {
169 auto iter = connections.find(connection);
170 if (iter == connections.end()) {
171 sideReference[connection.thisSide - 1]++;
172 connections.insert(connection);
173 }
174 }
175
176 void remove(const FaceConnection &connection)
177 {
178 auto iter = connections.find(connection);
179 if (iter != connections.end()) {
180 sideReference[connection.thisSide - 1]--;
181 connections.erase(iter);
182 }
183 }
184
185 // Number of sides defined by topology for this element entry
186 unsigned numSides{0};
187
188 // Number of references to given side based on entries to the connections member: for
189 // performance
190 std::vector<int> sideReference{};
191
192 // Collection of all face connections for the element. Number might be more than numSides
193 // based on solid/shell connections e.e solid connected to multiple stacked shells
194 std::set<FaceConnection> connections{};
195 };
196
197 virtual size_t get_num_elements() const = 0;
198 virtual int get_element_proc(const size_t elemIndex) const = 0;
199 virtual bool element_has_any_node_on_proc(const size_t elemIndex, int proc) const = 0;
200 virtual const std::string &get_element_block_name(const size_t elemIndex) const = 0;
201 virtual const std::vector<EntityId> &get_element_node_ids(const size_t elemIndex) const = 0;
202 virtual const Topology &get_element_topology(const size_t elemIndex) const = 0;
203 virtual EntityId get_element_id(const size_t elemIndex) const = 0;
204
206 {
207 ErrorHandler errorHandler = [](const std::ostringstream &errmsg) {
208 default_error_handler(errmsg);
209 };
210 set_error_handler(errorHandler);
211 }
212
213 virtual ~SideAdjacencyGraph() = default;
214
215 void create_graph(int proc = ANY_PROC) { create_graph({}, proc); }
216
217 void create_graph(const std::vector<std::string> &selectedBlocks, int proc = ANY_PROC)
218 {
219 m_indexGraph.clear();
220 std::vector<size_t> localAndAuraElementIndex =
221 get_local_and_aura_elements(selectedBlocks, proc);
222 std::unordered_map<EntityId, std::set<size_t>> elementsForNode =
223 get_elements_for_node_map(localAndAuraElementIndex);
224 build_side_connectivity_graph(localAndAuraElementIndex, elementsForNode);
225 }
226
227 void set_error_handler(ErrorHandler errorHandler) { m_errorHandler = errorHandler; }
228
229 void dump(std::ostream &out = std::cout)
230 {
231 for (const auto &iter : m_indexGraph) {
232 if (!iter.second.connections.empty()) {
233 out << "Element index: " << iter.first << std::endl;
234 }
235 for (const auto &entry : iter.second.connections) {
236 out << "\tConnected on side: " << entry.thisSide
237 << " to element index: " << entry.thatElement << " and side: " << entry.thatSide
238 << std::endl;
239 }
240 }
241 }
242
243 void dump(const std::vector<ElementData<EntityId, Topology>> &elemDataVec,
244 std::ostream &out = std::cout)
245 {
246 for (const auto &iter : m_indexGraph) {
247 if (!iter.second.connections.empty()) {
248 out << "Element: " << elemDataVec[iter.first].identifier << " {"
249 << elemDataVec[iter.first].topology << "}" << std::endl;
250 }
251 for (const auto &entry : iter.second.connections) {
252 out << "\tConnected on side: " << entry.thisSide
253 << " to element: " << elemDataVec[entry.thatElement].identifier << " {"
254 << elemDataVec[entry.thatElement].topology << "}" << " and side: " << entry.thatSide
255 << std::endl;
256 }
257 }
258 }
259
260 const FaceConnections &operator[](const IndexType elemIndex) const
261 {
262 auto it = m_indexGraph.find(elemIndex);
263
264 if (it == m_indexGraph.end()) {
265 std::ostringstream errmsg;
266 errmsg << "Could not find element index " << elemIndex;
267 m_errorHandler(errmsg);
268 }
269
270 return it->second;
271 }
272
273 size_t size() const { return m_indexGraph.size(); }
274
275 typename std::unordered_map<size_t, FaceConnections>::const_iterator begin() const
276 {
277 return m_indexGraph.begin();
278 }
279 typename std::unordered_map<size_t, FaceConnections>::const_iterator end() const
280 {
281 return m_indexGraph.end();
282 }
283
284 protected:
287
289 {
290 bool operator()(const std::string &lhs, const std::string &rhs)
291 {
292 return (strcasecmp(lhs.c_str(), rhs.c_str()) < 0);
293 };
294 };
295
296 // Current element and side under consideration
298 {
299 CurrentAdjacency(size_t elementIndex_, int side_) : elementIndex(elementIndex_), side(side_)
300 {
301 }
302
304 int side;
305
306 // Scratch space that is filled on-the-fly: for performance
307 std::vector<int> connectedSides;
308 };
309
311 const size_t elemIndex, int side,
312 const std::unordered_map<EntityId, std::set<size_t>> &elementsForNode)
313 {
314 std::set<size_t> neighbors;
315 std::vector<OrdinalType> sideNodeIndices =
317
318 const std::vector<EntityId> &elementNodeIds = get_element_node_ids(elemIndex);
319
320 if (!sideNodeIndices.empty()) {
321 EntityId firstSideNode = elementNodeIds[sideNodeIndices[0]];
322 neighbors = elementsForNode.at(firstSideNode);
323 }
324
325 for (size_t i = 1; i < sideNodeIndices.size(); ++i) {
326 OrdinalType sideNodeIndex = sideNodeIndices[i];
327 EntityId sideNode = elementNodeIds[sideNodeIndex];
328 const std::set<size_t> &sideNodeElementIndices = elementsForNode.at(sideNode);
329
330 std::set<size_t> intersection;
331 std::set_intersection(neighbors.begin(), neighbors.end(), sideNodeElementIndices.begin(),
332 sideNodeElementIndices.end(),
333 std::inserter(intersection, intersection.begin()));
334
335 neighbors = intersection;
336 }
337
338 return neighbors;
339 }
340
341 std::vector<EntityId> get_side_nodes(const size_t elemIndex, const int side)
342 {
343 std::vector<OrdinalType> sideNodeIndices =
345 std::vector<EntityId> sideNodes(sideNodeIndices.size());
346 const std::vector<EntityId> &elementNodeIds = get_element_node_ids(elemIndex);
347
348 for (size_t i = 0; i < sideNodeIndices.size(); ++i) {
349 OrdinalType sideNodeIndex = sideNodeIndices[i];
350 sideNodes[i] = elementNodeIds[sideNodeIndex];
351 }
352
353 return sideNodes;
354 }
355
356 std::vector<EntityId> get_sorted_side_nodes(const size_t elemIndex, const int side)
357 {
358 std::vector<EntityId> sideNodes = get_side_nodes(elemIndex, side);
359 std::sort(sideNodes.begin(), sideNodes.end());
360
361 return sideNodes;
362 }
363
364 // Fill scratch space with side connection info to neighbor element
366 size_t neighborElementIndex)
367 {
368 adjacency.connectedSides.clear();
369
370 std::vector<EntityId> sideNodes =
371 get_sorted_side_nodes(adjacency.elementIndex, adjacency.side);
372
373 for (int otherSide = 1;
374 otherSide <= get_element_topology(neighborElementIndex).num_face_sides();
375 ++otherSide) {
376 std::vector<EntityId> otherSideNodes =
377 get_sorted_side_nodes(neighborElementIndex, otherSide);
378 if (sideNodes == otherSideNodes) {
379 adjacency.connectedSides.push_back(otherSide);
380 }
381 }
382 }
383
384 // Find and validate connected sides to neighbor element
386 size_t neighborElementIndex)
387 {
388 internal_fill_sides_for_connected_element(adjacency, neighborElementIndex);
389
390 if (adjacency.connectedSides.empty()) {
391 std::ostringstream errmsg;
392 errmsg << "Neighboring reciprocity check for elements "
393 << get_element_id(adjacency.elementIndex) << " and "
394 << get_element_id(neighborElementIndex) << " failed.";
395 m_errorHandler(errmsg);
396 }
397 }
398
399 // Check to see if two set of nodes are equivalent based on permutation list
400 bool equivalent_node_permutation(const std::vector<EntityId> &controlNodes,
401 const std::vector<EntityId> &permutedNodes,
402 const std::vector<OrdinalType> &permutationOrdinals)
403 {
404 const size_t numNodes = permutationOrdinals.size();
405
406 if ((numNodes > permutedNodes.size()) || (numNodes > controlNodes.size())) {
407 return false;
408 }
409
410 bool equivalent = true;
411 for (size_t i = 0; equivalent && i < numNodes; ++i) {
412 equivalent = controlNodes[permutationOrdinals[i]] == permutedNodes[i];
413 }
414
415 return equivalent;
416 }
417
418 // Get the permutation that makes one set of nodes equivalent to the other
419 std::pair<bool, PermutationType> get_permutation(const Topology &topology,
420 const std::vector<EntityId> &controlNodes,
421 const std::vector<EntityId> &permutedNodes,
422 PermutationType numPermutations)
423 {
425 bool equivalent = false;
426
427 if (controlNodes.size() != permutedNodes.size())
428 return std::make_pair(equivalent, permutation);
429
430 if (numPermutations > topology.num_permutations()) {
431 std::ostringstream errmsg;
432 errmsg << "Invalid number of permutations to check: " << numPermutations;
433 m_errorHandler(errmsg);
434 }
435
436 std::vector<OrdinalType> permutationOrdinals;
437
438 for (PermutationType i = 0; i < numPermutations; ++i) {
439 if (topology.fill_permutation_indices(i, permutationOrdinals)) {
440 if (equivalent_node_permutation(controlNodes, permutedNodes, permutationOrdinals)) {
441 equivalent = true;
442 permutation = i;
443 break;
444 }
445 }
446 }
447
448 return std::make_pair(equivalent, permutation);
449 }
450
451 std::pair<bool, PermutationType> get_permutation(const Topology &topology,
452 const std::vector<EntityId> &controlNodes,
453 const std::vector<EntityId> &permutedNodes)
454 {
455 return get_permutation(topology, controlNodes, permutedNodes, topology.num_permutations());
456 }
457
458 std::pair<bool, PermutationType>
459 get_positive_permutation(const Topology &topology, const std::vector<EntityId> &controlNodes,
460 const std::vector<EntityId> &permutedNodes)
461 {
462 return get_permutation(topology, controlNodes, permutedNodes,
463 topology.num_positive_permutations());
464 }
465
466 bool has_same_polarity(const size_t thisElem, const int thisSide, const size_t thatElem,
467 const int thatSide)
468 {
469 std::vector<EntityId> thisNodes = get_side_nodes(thisElem, thisSide);
470 std::vector<EntityId> thatNodes = get_side_nodes(thatElem, thatSide);
471
472 const Topology &sideTopo = get_element_topology(thisElem).side_topology(thisSide);
473 std::pair<bool, PermutationType> result =
474 get_positive_permutation(sideTopo, thisNodes, thatNodes);
475
476 bool samePolarity = result.first;
477 return samePolarity;
478 }
479
480 // Check for valid element/side pair
481 bool verify_entry(IndexType elemIndex, int side)
482 {
483 if (INVALID_INDEX == elemIndex)
484 return false;
485
486 auto it = m_indexGraph.find(elemIndex);
487 if (it == m_indexGraph.end())
488 return false;
489
490 int numSides = it->second.numSides;
491 if (side < 1 || side > numSides)
492 return false;
493
494 return true;
495 }
496
497 // Given a current element/side pair, find all other defined reciprocal element connections
498 std::vector<FaceConnection> get_reciprocity(CurrentAdjacency &adjacency)
499 {
500 std::vector<FaceConnection> reciprocity;
501 const FaceConnections &thisEntry = m_indexGraph[adjacency.elementIndex];
502
503 for (const FaceConnection &connectionToThatElement : thisEntry.connections) {
504 IndexType thatIndex = connectionToThatElement.thatElement;
505 int thatSide = connectionToThatElement.thatSide;
506
507 if (!verify_entry(thatIndex, thatSide))
508 continue;
509 const FaceConnections &thatEntry = m_indexGraph[thatIndex];
510
511 const FaceConnection connectionToThisElement(thatSide, adjacency.elementIndex,
512 adjacency.side);
513 if (std::binary_search(thatEntry.connections.begin(), thatEntry.connections.end(),
514 connectionToThisElement)) {
515 reciprocity.push_back(connectionToThatElement);
516 }
517 }
518
519 return reciprocity;
520 }
521
522 // Break all current reciprocal connections to this element/side pair
524 {
525 std::vector<FaceConnection> reciprocity = get_reciprocity(adjacency);
526
527 if (!reciprocity.empty()) {
528 FaceConnections &thisEntry = m_indexGraph[adjacency.elementIndex];
529
530 for (const FaceConnection &connectionToThatElement : reciprocity) {
531 IndexType thatIndex = connectionToThatElement.thatElement;
532 int thatSide = connectionToThatElement.thatSide;
533
534 FaceConnections &thatEntry = m_indexGraph[thatIndex];
535 FaceConnection connectionToThisElement(thatSide, adjacency.elementIndex,
536 adjacency.side);
537
538 thisEntry.remove(connectionToThatElement);
539 thatEntry.remove(connectionToThisElement);
540 }
541 }
542 }
543
544 inline bool is_shell_shell_connection(const Topology &thisElemTopology,
545 const Topology &thatElemTopology)
546 {
547 return thisElemTopology.is_shell() && thatElemTopology.is_shell();
548 }
549
550 inline bool is_shell_shell_connection(const size_t thisElem, const size_t thatElem)
551 {
553 get_element_topology(thatElem));
554 }
555
556 inline bool is_shell_solid_connection(const Topology &thisElemTopology,
557 const Topology &thatElemTopology)
558 {
559 return thisElemTopology.is_shell() && !thatElemTopology.is_shell();
560 }
561
562 inline bool is_shell_solid_connection(const size_t thisElem, const size_t thatElem)
563 {
565 get_element_topology(thatElem));
566 }
567
568 inline bool is_solid_shell_connection(const Topology &thisElemTopology,
569 const Topology &thatElemTopology)
570 {
571 return !thisElemTopology.is_shell() && thatElemTopology.is_shell();
572 }
573
574 inline bool is_solid_shell_connection(const size_t thisElem, const size_t thatElem)
575 {
577 get_element_topology(thatElem));
578 }
579
580 inline bool is_solid_solid_connection(const Topology &thisElemTopology,
581 const Topology &thatElemTopology)
582 {
583 return !thisElemTopology.is_shell() && !thatElemTopology.is_shell();
584 }
585
586 inline bool is_solid_solid_connection(const size_t thisElem, const size_t thatElem)
587 {
589 get_element_topology(thatElem));
590 }
591
592 using Criterion = std::function<bool(const Topology &topo1, const Topology &topo2)>;
593
594 bool has_connection_type_on_side(size_t thisIndex, int thisSide, Criterion criterion)
595 {
596 const FaceConnections &thisEntry = m_indexGraph[thisIndex];
597
598 for (const FaceConnection &connection : thisEntry.connections) {
599 IndexType thatIndex = connection.thatElement;
600
601 if (connection.thisSide != thisSide || INVALID_INDEX == thatIndex)
602 continue;
603
604 if (criterion(get_element_topology(thisIndex), get_element_topology(thatIndex))) {
605 return true;
606 }
607 }
608
609 return false;
610 }
611
612 bool has_any_shell_connection_on_side(size_t thisIndex, int thisSide)
613 {
614 Criterion criterion = [&](const Topology & /* topo1 */, const Topology &topo2) {
615 return topo2.is_shell();
616 };
617
618 return has_connection_type_on_side(thisIndex, thisSide, criterion);
619 }
620
621 bool has_shell_shell_connection_on_side(size_t thisIndex, int thisSide)
622 {
623 Criterion criterion = [&](const Topology &topo1, const Topology &topo2) {
624 return is_shell_shell_connection(topo1, topo2);
625 };
626
627 return has_connection_type_on_side(thisIndex, thisSide, criterion);
628 }
629
630 bool has_shell_solid_connection_on_side(size_t thisIndex, int thisSide)
631 {
632 Criterion criterion = [&](const Topology &topo1, const Topology &topo2) {
633 return is_shell_solid_connection(topo1, topo2);
634 };
635
636 return has_connection_type_on_side(thisIndex, thisSide, criterion);
637 }
638
639 bool has_solid_shell_connection_on_side(size_t thisIndex, int thisSide)
640 {
641 Criterion criterion = [&](const Topology &topo1, const Topology &topo2) {
642 return is_solid_shell_connection(topo1, topo2);
643 };
644
645 return has_connection_type_on_side(thisIndex, thisSide, criterion);
646 }
647
648 bool has_solid_solid_connection_on_side(size_t thisIndex, int thisSide)
649 {
650 Criterion criterion = [&](const Topology &topo1, const Topology &topo2) {
651 return is_solid_solid_connection(topo1, topo2);
652 };
653
654 return has_connection_type_on_side(thisIndex, thisSide, criterion);
655 }
656
657 void add_connection(CurrentAdjacency &adjacency, size_t connectedElementIndex, int otherSide)
658 {
659 const size_t thisElem = adjacency.elementIndex;
660 const size_t thatElem = connectedElementIndex;
661
662 bool doConnect = false;
663 bool breakConnection = false;
664
665 if (is_shell_solid_connection(thisElem, thatElem)) {
666 doConnect = !has_same_polarity(thisElem, adjacency.side, thatElem, otherSide);
667
668 if (has_solid_solid_connection_on_side(connectedElementIndex, otherSide)) {
669 breakConnection = doConnect;
670 }
671 }
672 else if (is_solid_shell_connection(thisElem, thatElem)) {
673 doConnect = !has_same_polarity(thisElem, adjacency.side, thatElem, otherSide);
674
675 if (has_solid_solid_connection_on_side(adjacency.elementIndex, adjacency.side)) {
676 breakConnection = doConnect;
677 }
678 }
679 else if (is_solid_solid_connection(thisElem, thatElem)) {
680 doConnect = !has_any_shell_connection_on_side(adjacency.elementIndex, adjacency.side) &&
681 !has_any_shell_connection_on_side(connectedElementIndex, otherSide) &&
682 !has_same_polarity(thisElem, adjacency.side, thatElem, otherSide);
683 }
684
685 if (breakConnection) {
687 }
688
689 if (doConnect) {
690 m_indexGraph[adjacency.elementIndex].add(
691 FaceConnection(adjacency.side, connectedElementIndex, otherSide));
692 m_indexGraph[connectedElementIndex].add(
693 FaceConnection(otherSide, adjacency.elementIndex, adjacency.side));
694 }
695 }
696
697 void set_side_connectivity(CurrentAdjacency &adjacency, size_t connectedElementIndex)
698 {
699 fill_sides_for_connected_element(adjacency, connectedElementIndex);
700
701 for (int otherSide : adjacency.connectedSides) {
702 add_connection(adjacency, connectedElementIndex, otherSide);
703 }
704 }
705
707 IndexType connectedElemIndex2)
708 {
709 if (get_element_topology(connectedElemIndex1).is_shell() &&
710 get_element_topology(connectedElemIndex2).is_shell()) {
711 if (get_element_proc(connectedElemIndex1) != get_element_proc(connectedElemIndex2)) {
712 std::ostringstream errmsg;
713 errmsg << "Invalid proc ownership for co-incident shells "
714 << get_element_id(connectedElemIndex1) << " (proc "
715 << get_element_proc(connectedElemIndex1) << ") and "
716 << get_element_proc(connectedElemIndex2) << " (proc "
717 << get_element_proc(connectedElemIndex2) << ")."
718 << " Co-incident shells must all exist on the same processor";
719 m_errorHandler(errmsg);
720 }
721 }
722 }
723
725 CurrentAdjacency &adjacency,
726 const std::unordered_map<EntityId, std::set<size_t>> &elementsForNode)
727 {
728 int side = adjacency.side;
729
730 std::set<size_t> elementIndicesConnectedToSide =
732 elementsForNode);
733 for (size_t connectedElementIndex : elementIndicesConnectedToSide) {
734 if (connectedElementIndex != adjacency.elementIndex) {
735 enforce_coincident_shell_ownership(adjacency.elementIndex, connectedElementIndex);
736 set_side_connectivity(adjacency, connectedElementIndex);
737 }
738 }
739 }
740
742 const std::vector<size_t> &elementIndices,
743 const std::unordered_map<EntityId, std::set<size_t>> &elementsForNode)
744 {
746
747 for (size_t elementIndex : elementIndices) {
748 int numSides = get_element_topology(elementIndex).num_face_sides();
749 for (int side = 1; side <= numSides; ++side) {
750 if (m_indexGraph[elementIndex].sideReference[side - 1] == 0) {
751 CurrentAdjacency adjacency(elementIndex, side);
752 process_side_connectivity(adjacency, elementsForNode);
753 }
754 }
755 }
756 }
757
758 void initialize_side_connectivity_graph(const std::vector<size_t> &elementIndices)
759 {
760 for (size_t elementIndex : elementIndices) {
761 m_indexGraph[elementIndex] =
762 FaceConnections(get_element_topology(elementIndex).num_face_sides());
763 }
764 }
765
766 std::unordered_map<EntityId, std::set<size_t>>
767 get_elements_for_node_map(const std::vector<size_t> &elementIndices)
768 {
769 std::unordered_map<EntityId, std::set<size_t>> elementsForNode;
770 for (size_t index : elementIndices) {
771 for (const EntityId nodeId : get_element_node_ids(index)) {
772 elementsForNode[nodeId].insert(index);
773 }
774 }
775
776 return elementsForNode;
777 }
778
779 bool element_is_in_selected_blocks(const size_t elemIndex,
780 const std::vector<std::string> &sortedSelectedBlocks)
781 {
782 if (sortedSelectedBlocks.empty())
783 return true;
784
785 const std::string &partName = get_element_block_name(elemIndex);
786
787 return std::binary_search(sortedSelectedBlocks.begin(), sortedSelectedBlocks.end(),
788 partName, StringCaseCompLess());
789 }
790
791 bool is_selected_element(const size_t elemIndex,
792 const std::vector<std::string> &sortedSelectedBlocks, int proc)
793 {
794 bool isGloballySelected = (ANY_PROC == proc);
795 bool isLocallySelected = (get_element_proc(elemIndex) == proc);
796 bool hasLocalNode = element_has_any_node_on_proc(elemIndex, proc);
797 bool isInSelectedBlocks = element_is_in_selected_blocks(elemIndex, sortedSelectedBlocks);
798
799 return isInSelectedBlocks && (isGloballySelected || isLocallySelected || hasLocalNode);
800 }
801
802 std::vector<size_t>
803 get_local_and_aura_elements(const std::vector<std::string> &selectedBlocks, int proc)
804 {
805 std::vector<size_t> localAndAuraElementIndex;
806 localAndAuraElementIndex.reserve(get_num_elements());
807
808 std::vector<std::string> sortedSelectedBlocks;
809 for (const std::string &block : selectedBlocks) {
810 sortedSelectedBlocks.push_back(block);
811 }
812 std::sort(sortedSelectedBlocks.begin(), sortedSelectedBlocks.end(), StringCaseCompLess());
813
814 for (size_t i = 0; i < get_num_elements(); ++i) {
815 if (is_selected_element(i, sortedSelectedBlocks, proc)) {
816 localAndAuraElementIndex.push_back(i);
817 }
818 }
819
820 localAndAuraElementIndex.resize(localAndAuraElementIndex.size());
821 return localAndAuraElementIndex;
822 }
823
825 std::unordered_map<size_t, FaceConnections> m_indexGraph;
826 };
827
828 } // namespace text_mesh
829} // namespace Iotm
std::vector< Ordinal > side_topology_node_indices(unsigned side) const
Definition Iotm_TextMeshTopologyMapping.h:131
static constexpr Permutation InvalidPermutation
Definition Iotm_TextMeshTopologyMapping.h:39
uint8_t Permutation
Definition Iotm_TextMeshTopologyMapping.h:36
unsigned num_positive_permutations() const
Definition Iotm_TextMeshTopologyMapping.h:152
bool is_shell() const
Definition Iotm_TextMeshTopologyMapping.h:148
bool fill_permutation_indices(Permutation permutation, std::vector< Ordinal > &nodeOrdinalVector) const
Definition Iotm_TextMeshTopologyMapping.h:167
int num_face_sides() const
Definition Iotm_TextMeshTopologyMapping.h:83
uint16_t Ordinal
Definition Iotm_TextMeshTopologyMapping.h:35
unsigned num_permutations() const
Definition Iotm_TextMeshTopologyMapping.h:150
const TopologyMapEntry & side_topology(unsigned side) const
Definition Iotm_TextMeshTopologyMapping.h:115
void create_graph(int proc=ANY_PROC)
Definition Iotm_TextMeshAdjacencyGraph.h:215
size_t size() const
Definition Iotm_TextMeshAdjacencyGraph.h:273
void add_connection(CurrentAdjacency &adjacency, size_t connectedElementIndex, int otherSide)
Definition Iotm_TextMeshAdjacencyGraph.h:657
bool element_is_in_selected_blocks(const size_t elemIndex, const std::vector< std::string > &sortedSelectedBlocks)
Definition Iotm_TextMeshAdjacencyGraph.h:779
static constexpr int INVALID_SIDE
Definition Iotm_TextMeshAdjacencyGraph.h:97
virtual size_t get_num_elements() const =0
ErrorHandler m_errorHandler
Definition Iotm_TextMeshAdjacencyGraph.h:824
virtual const Topology & get_element_topology(const size_t elemIndex) const =0
bool is_shell_shell_connection(const size_t thisElem, const size_t thatElem)
Definition Iotm_TextMeshAdjacencyGraph.h:550
bool is_shell_solid_connection(const size_t thisElem, const size_t thatElem)
Definition Iotm_TextMeshAdjacencyGraph.h:562
void create_graph(const std::vector< std::string > &selectedBlocks, int proc=ANY_PROC)
Definition Iotm_TextMeshAdjacencyGraph.h:217
bool is_selected_element(const size_t elemIndex, const std::vector< std::string > &sortedSelectedBlocks, int proc)
Definition Iotm_TextMeshAdjacencyGraph.h:791
virtual const std::string & get_element_block_name(const size_t elemIndex) const =0
bool has_any_shell_connection_on_side(size_t thisIndex, int thisSide)
Definition Iotm_TextMeshAdjacencyGraph.h:612
void fill_sides_for_connected_element(CurrentAdjacency &adjacency, size_t neighborElementIndex)
Definition Iotm_TextMeshAdjacencyGraph.h:385
void break_reciprocal_connections(CurrentAdjacency &adjacency)
Definition Iotm_TextMeshAdjacencyGraph.h:523
void build_side_connectivity_graph(const std::vector< size_t > &elementIndices, const std::unordered_map< EntityId, std::set< size_t > > &elementsForNode)
Definition Iotm_TextMeshAdjacencyGraph.h:741
const FaceConnections & operator[](const IndexType elemIndex) const
Definition Iotm_TextMeshAdjacencyGraph.h:260
std::vector< FaceConnection > get_reciprocity(CurrentAdjacency &adjacency)
Definition Iotm_TextMeshAdjacencyGraph.h:498
bool has_same_polarity(const size_t thisElem, const int thisSide, const size_t thatElem, const int thatSide)
Definition Iotm_TextMeshAdjacencyGraph.h:466
std::pair< bool, PermutationType > get_permutation(const Topology &topology, const std::vector< EntityId > &controlNodes, const std::vector< EntityId > &permutedNodes, PermutationType numPermutations)
Definition Iotm_TextMeshAdjacencyGraph.h:419
void initialize_side_connectivity_graph(const std::vector< size_t > &elementIndices)
Definition Iotm_TextMeshAdjacencyGraph.h:758
virtual EntityId get_element_id(const size_t elemIndex) const =0
std::unordered_map< EntityId, std::set< size_t > > get_elements_for_node_map(const std::vector< size_t > &elementIndices)
Definition Iotm_TextMeshAdjacencyGraph.h:767
typename Topology::Ordinal OrdinalType
Definition Iotm_TextMeshAdjacencyGraph.h:285
std::vector< size_t > get_local_and_aura_elements(const std::vector< std::string > &selectedBlocks, int proc)
Definition Iotm_TextMeshAdjacencyGraph.h:803
virtual int get_element_proc(const size_t elemIndex) const =0
bool equivalent_node_permutation(const std::vector< EntityId > &controlNodes, const std::vector< EntityId > &permutedNodes, const std::vector< OrdinalType > &permutationOrdinals)
Definition Iotm_TextMeshAdjacencyGraph.h:400
void internal_fill_sides_for_connected_element(CurrentAdjacency &adjacency, size_t neighborElementIndex)
Definition Iotm_TextMeshAdjacencyGraph.h:365
bool has_solid_solid_connection_on_side(size_t thisIndex, int thisSide)
Definition Iotm_TextMeshAdjacencyGraph.h:648
bool is_shell_solid_connection(const Topology &thisElemTopology, const Topology &thatElemTopology)
Definition Iotm_TextMeshAdjacencyGraph.h:556
void enforce_coincident_shell_ownership(IndexType connectedElemIndex1, IndexType connectedElemIndex2)
Definition Iotm_TextMeshAdjacencyGraph.h:706
std::unordered_map< size_t, FaceConnections >::const_iterator end() const
Definition Iotm_TextMeshAdjacencyGraph.h:279
std::pair< bool, PermutationType > get_permutation(const Topology &topology, const std::vector< EntityId > &controlNodes, const std::vector< EntityId > &permutedNodes)
Definition Iotm_TextMeshAdjacencyGraph.h:451
bool has_solid_shell_connection_on_side(size_t thisIndex, int thisSide)
Definition Iotm_TextMeshAdjacencyGraph.h:639
int64_t IndexType
Definition Iotm_TextMeshAdjacencyGraph.h:94
void dump(std::ostream &out=std::cout)
Definition Iotm_TextMeshAdjacencyGraph.h:229
void dump(const std::vector< ElementData< EntityId, Topology > > &elemDataVec, std::ostream &out=std::cout)
Definition Iotm_TextMeshAdjacencyGraph.h:243
std::vector< EntityId > get_side_nodes(const size_t elemIndex, const int side)
Definition Iotm_TextMeshAdjacencyGraph.h:341
bool verify_entry(IndexType elemIndex, int side)
Definition Iotm_TextMeshAdjacencyGraph.h:481
bool is_solid_shell_connection(const size_t thisElem, const size_t thatElem)
Definition Iotm_TextMeshAdjacencyGraph.h:574
static constexpr IndexType INVALID_INDEX
Definition Iotm_TextMeshAdjacencyGraph.h:98
std::unordered_map< size_t, FaceConnections >::const_iterator begin() const
Definition Iotm_TextMeshAdjacencyGraph.h:275
std::pair< bool, PermutationType > get_positive_permutation(const Topology &topology, const std::vector< EntityId > &controlNodes, const std::vector< EntityId > &permutedNodes)
Definition Iotm_TextMeshAdjacencyGraph.h:459
std::function< bool(const Topology &topo1, const Topology &topo2)> Criterion
Definition Iotm_TextMeshAdjacencyGraph.h:592
bool is_solid_shell_connection(const Topology &thisElemTopology, const Topology &thatElemTopology)
Definition Iotm_TextMeshAdjacencyGraph.h:568
void process_side_connectivity(CurrentAdjacency &adjacency, const std::unordered_map< EntityId, std::set< size_t > > &elementsForNode)
Definition Iotm_TextMeshAdjacencyGraph.h:724
bool has_shell_shell_connection_on_side(size_t thisIndex, int thisSide)
Definition Iotm_TextMeshAdjacencyGraph.h:621
virtual bool element_has_any_node_on_proc(const size_t elemIndex, int proc) const =0
std::unordered_map< size_t, FaceConnections > m_indexGraph
Definition Iotm_TextMeshAdjacencyGraph.h:825
bool has_shell_solid_connection_on_side(size_t thisIndex, int thisSide)
Definition Iotm_TextMeshAdjacencyGraph.h:630
bool is_solid_solid_connection(const size_t thisElem, const size_t thatElem)
Definition Iotm_TextMeshAdjacencyGraph.h:586
typename Topology::Permutation PermutationType
Definition Iotm_TextMeshAdjacencyGraph.h:286
virtual const std::vector< EntityId > & get_element_node_ids(const size_t elemIndex) const =0
static constexpr int ANY_PROC
Definition Iotm_TextMeshAdjacencyGraph.h:96
void set_side_connectivity(CurrentAdjacency &adjacency, size_t connectedElementIndex)
Definition Iotm_TextMeshAdjacencyGraph.h:697
bool has_connection_type_on_side(size_t thisIndex, int thisSide, Criterion criterion)
Definition Iotm_TextMeshAdjacencyGraph.h:594
void set_error_handler(ErrorHandler errorHandler)
Definition Iotm_TextMeshAdjacencyGraph.h:227
bool is_shell_shell_connection(const Topology &thisElemTopology, const Topology &thatElemTopology)
Definition Iotm_TextMeshAdjacencyGraph.h:544
bool is_solid_solid_connection(const Topology &thisElemTopology, const Topology &thatElemTopology)
Definition Iotm_TextMeshAdjacencyGraph.h:580
std::vector< EntityId > get_sorted_side_nodes(const size_t elemIndex, const int side)
Definition Iotm_TextMeshAdjacencyGraph.h:356
std::set< size_t > get_element_indices_with_common_nodes_on_side(const size_t elemIndex, int side, const std::unordered_map< EntityId, std::set< size_t > > &elementsForNode)
Definition Iotm_TextMeshAdjacencyGraph.h:310
SideAdjacencyGraph()
Definition Iotm_TextMeshAdjacencyGraph.h:205
Definition Iotm_TextMeshAdjacencyGraph.h:39
std::function< void(const std::ostringstream &)> ErrorHandler
Definition Iotm_TextMeshAdjacencyGraph.h:41
void default_error_handler(const std::ostringstream &message)
Definition Iotm_TextMeshFuncs.h:40
A namespace for the textmesh database format.
Definition Iotm_DatabaseIO.C:95
TopologyMapEntry Topology
Definition Iotm_TextMesh.h:34
int64_t EntityId
Definition Iotm_TextMesh.h:33
Definition Iotm_TextMeshDataTypes.h:276
Definition Iotm_TextMeshAdjacencyGraph.h:298
std::vector< int > connectedSides
Definition Iotm_TextMeshAdjacencyGraph.h:307
size_t elementIndex
Definition Iotm_TextMeshAdjacencyGraph.h:303
CurrentAdjacency(size_t elementIndex_, int side_)
Definition Iotm_TextMeshAdjacencyGraph.h:299
int side
Definition Iotm_TextMeshAdjacencyGraph.h:304
Definition Iotm_TextMeshAdjacencyGraph.h:101
bool operator==(const FaceConnection &rhs) const
Definition Iotm_TextMeshAdjacencyGraph.h:110
bool operator<(const FaceConnection &rhs) const
Definition Iotm_TextMeshAdjacencyGraph.h:122
bool operator!=(const FaceConnection &rhs) const
Definition Iotm_TextMeshAdjacencyGraph.h:116
int thisSide
Definition Iotm_TextMeshAdjacencyGraph.h:135
IndexType thatElement
Definition Iotm_TextMeshAdjacencyGraph.h:136
int thatSide
Definition Iotm_TextMeshAdjacencyGraph.h:137
FaceConnection()
Definition Iotm_TextMeshAdjacencyGraph.h:102
FaceConnection(int thisSide_, IndexType otherElement_, int otherSide_)
Definition Iotm_TextMeshAdjacencyGraph.h:105
Definition Iotm_TextMeshAdjacencyGraph.h:141
FaceConnections(unsigned numSides_)
Definition Iotm_TextMeshAdjacencyGraph.h:143
std::vector< int > sideReference
Definition Iotm_TextMeshAdjacencyGraph.h:190
bool has_any_connection(int thisSide, IndexType thatElem) const
Definition Iotm_TextMeshAdjacencyGraph.h:157
bool has_any_connection(int thisSide) const
Definition Iotm_TextMeshAdjacencyGraph.h:148
std::set< FaceConnection > connections
Definition Iotm_TextMeshAdjacencyGraph.h:194
FaceConnections()
Definition Iotm_TextMeshAdjacencyGraph.h:142
unsigned numSides
Definition Iotm_TextMeshAdjacencyGraph.h:186
void remove(const FaceConnection &connection)
Definition Iotm_TextMeshAdjacencyGraph.h:176
void add(const FaceConnection &connection)
Definition Iotm_TextMeshAdjacencyGraph.h:167
Definition Iotm_TextMeshAdjacencyGraph.h:289
bool operator()(const std::string &lhs, const std::string &rhs)
Definition Iotm_TextMeshAdjacencyGraph.h:290
Definition Iotm_TextMeshDataTypes.h:314