From 97c6b24b8d0ecde73b68fbc8001c9b920b4518c3 Mon Sep 17 00:00:00 2001 From: Thomas Fischer Date: Fri, 9 Jul 2021 16:25:21 +0200 Subject: [PATCH 1/4] [MeL/Mesh] Make setNodesConnectedByElements a free fct. Rename setNodesConnectedByElements to calculateNodesConnectedByElements and make it a free function. This 'out sourcing' makes it possible to compute the connection information only when it is required. --- MeshLib/Mesh.cpp | 73 ++++++++++++++++++++++++------------------------ MeshLib/Mesh.h | 8 +++--- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/MeshLib/Mesh.cpp b/MeshLib/Mesh.cpp index 6cf57cec18..9e81ba0cee 100644 --- a/MeshLib/Mesh.cpp +++ b/MeshLib/Mesh.cpp @@ -62,7 +62,6 @@ Mesh::Mesh(std::string name, } this->setDimension(); this->setElementsConnectedToNodes(); - this->setNodesConnectedByElements(); this->setElementNeighbors(); this->calcEdgeLengthRange(); @@ -103,7 +102,6 @@ Mesh::Mesh(const Mesh& mesh) this->setDimension(); } this->setElementsConnectedToNodes(); - // this->setNodesConnectedByElements(); this->setElementNeighbors(); } @@ -236,40 +234,6 @@ void Mesh::setElementNeighbors() } } -void Mesh::setNodesConnectedByElements() -{ - // Allocate temporary space for adjacent nodes. - std::vector adjacent_nodes; - for (Node* const node : _nodes) - { - adjacent_nodes.clear(); - - // Get all elements, to which this node is connected. - std::vector const& conn_elems = node->getElements(); - - // And collect all elements' nodes. - for (Element const* const element : conn_elems) - { - Node* const* const single_elem_nodes = element->getNodes(); - std::size_t const nnodes = element->getNumberOfNodes(); - for (std::size_t n = 0; n < nnodes; n++) - { - adjacent_nodes.push_back(single_elem_nodes[n]); - } - } - - // Make nodes unique and sorted by their ids. - // This relies on the node's id being equivalent to it's address. - std::sort(adjacent_nodes.begin(), adjacent_nodes.end(), - [](Node* a, Node* b) { return a->getID() < b->getID(); }); - auto const last = - std::unique(adjacent_nodes.begin(), adjacent_nodes.end()); - adjacent_nodes.erase(last, adjacent_nodes.end()); - - node->setConnectedNodes(adjacent_nodes); - } -} - void Mesh::checkNonlinearNodeIDs() const { for (MeshLib::Element const* e : _elements) @@ -386,4 +350,41 @@ std::unique_ptr createMeshFromElementSelection( return mesh; } + +std::vector> calculateNodesConnectedByElements( + Mesh const& mesh) +{ + std::vector> nodes_connected_by_elements; + auto const& nodes = mesh.getNodes(); + nodes_connected_by_elements.resize(nodes.size()); + for (std::size_t i = 0; i < nodes.size(); ++i) + { + auto& adjacent_nodes = nodes_connected_by_elements[i]; + auto const* node = nodes[i]; + + // Get all elements, to which this node is connected. + auto const& connected_elements = node->getElements(); + + // And collect all elements' nodes. + for (Element const* const element : connected_elements) + { + Node* const* const single_elem_nodes = element->getNodes(); + std::size_t const nnodes = element->getNumberOfNodes(); + for (std::size_t n = 0; n < nnodes; n++) + { + adjacent_nodes.push_back(single_elem_nodes[n]); + } + } + + // Make nodes unique and sorted by their ids. + // This relies on the node's id being equivalent to it's address. + std::sort(adjacent_nodes.begin(), adjacent_nodes.end(), + [](Node* a, Node* b) { return a->getID() < b->getID(); }); + auto const last = + std::unique(adjacent_nodes.begin(), adjacent_nodes.end()); + adjacent_nodes.erase(last, adjacent_nodes.end()); + } + return nodes_connected_by_elements; +} + } // namespace MeshLib diff --git a/MeshLib/Mesh.h b/MeshLib/Mesh.h index 2056afbe4d..0cca934b12 100644 --- a/MeshLib/Mesh.h +++ b/MeshLib/Mesh.h @@ -150,10 +150,6 @@ protected: /// Note: Using this implementation, an element e can only have neighbors that have the same dimensionality as e. void setElementNeighbors(); - /// Computes the element-connectivity of nodes. Two nodes i and j are - /// connected if they are shared by an element. - void setNodesConnectedByElements(); - /// Check if all the nonlinear nodes are stored at the end of the node vector void checkNonlinearNodeIDs() const; @@ -177,6 +173,10 @@ protected: bool _is_axially_symmetric = false; }; /* class */ +/// Computes the element-connectivity of nodes. Two nodes i and j are +/// connected if they are shared by an element. +std::vector> calculateNodesConnectedByElements( + Mesh const& mesh); /// Meshes are equal if their id's are equal. inline bool operator==(Mesh const& a, Mesh const& b) -- GitLab From b7e149c0a9dbbea6513cfd5f8883ee3c8bb353e1 Mon Sep 17 00:00:00 2001 From: Thomas Fischer Date: Fri, 9 Jul 2021 16:27:27 +0200 Subject: [PATCH 2/4] [A | MeL | NL | T] Use fct. calculateNodesConnectedByElements. --- Applications/Utils/MeshEdit/moveMeshNodes.cpp | 5 +- Applications/Utils/MeshEdit/queryMesh.cpp | 6 +- MeshLib/NodeAdjacencyTable.h | 37 +++-- NumLib/DOF/ComputeSparsityPattern.cpp | 2 +- Tests/MeshLib/TestNodeAdjacencyTable.cpp | 6 +- Tests/MeshLib/TestQuadraticMesh.cpp | 129 +++++++++++------- 6 files changed, 105 insertions(+), 80 deletions(-) diff --git a/Applications/Utils/MeshEdit/moveMeshNodes.cpp b/Applications/Utils/MeshEdit/moveMeshNodes.cpp index 33e13bdf7a..d7c4a9c6cd 100644 --- a/Applications/Utils/MeshEdit/moveMeshNodes.cpp +++ b/Applications/Utils/MeshEdit/moveMeshNodes.cpp @@ -180,10 +180,11 @@ int main(int argc, char* argv[]) elevation[i] = (*nodes[i])[2]; } + auto const& connections = + MeshLib::calculateNodesConnectedByElements(*mesh); for (std::size_t i = 0; i < nNodes; i++) { - const std::vector conn_nodes( - nodes[i]->getConnectedNodes()); + auto const& conn_nodes(connections[nodes[i]->getID()]); const unsigned nConnNodes(conn_nodes.size()); elevation[i] = (2 * (*nodes[i])[2]); for (std::size_t j = 0; j < nConnNodes; ++j) diff --git a/Applications/Utils/MeshEdit/queryMesh.cpp b/Applications/Utils/MeshEdit/queryMesh.cpp index e844ce8180..1ebd40fde9 100644 --- a/Applications/Utils/MeshEdit/queryMesh.cpp +++ b/Applications/Utils/MeshEdit/queryMesh.cpp @@ -114,6 +114,7 @@ int main(int argc, char* argv[]) INFO("{:s}", out.str()); } + auto const& connections = MeshLib::calculateNodesConnectedByElements(*mesh); for (auto node_id : selected_node_ids) { std::stringstream out; @@ -130,8 +131,9 @@ int main(int argc, char* argv[]) out << ele->getID() << " "; } out << std::endl; - out << "Connected nodes (" << node->getConnectedNodes().size() << "): "; - for (auto nd : node->getConnectedNodes()) + out << "Connected nodes (" << connections[node->getID()].size() + << "): "; + for (auto nd : connections[node->getID()]) { out << nd->getID() << " "; } diff --git a/MeshLib/NodeAdjacencyTable.h b/MeshLib/NodeAdjacencyTable.h index da9f1fcc4a..507630db93 100644 --- a/MeshLib/NodeAdjacencyTable.h +++ b/MeshLib/NodeAdjacencyTable.h @@ -13,32 +13,27 @@ #include #include -#include "Node.h" - +#include "Mesh.h" namespace MeshLib { - /// Representation of topological node adjacency. /// -/// The topological sparsity pattern in the context of FEM is defined in terms of -/// supports of the nodal functions. Especially, two nodes i and j are called +/// The topological sparsity pattern in the context of FEM is defined in terms +/// of supports of the nodal functions. Especially, two nodes i and j are called /// adjacent if and only if there is a mesh element E including nodes i and j. /// This information is represented by the NodeAdjacenceTable. /// /// The topological adjacency of nodes is created by -/// Mesh::setNodesConnectedByElements() which is usually called upon mesh -/// construction. -class -NodeAdjacencyTable +/// MeshLib::calculateNodesConnectedByElements(). +class NodeAdjacencyTable final { public: - explicit - NodeAdjacencyTable(std::vector const& nodes) + explicit NodeAdjacencyTable(Mesh const& mesh) { - _data.resize(nodes.size()); + _data.resize(mesh.getNodes().size()); - createTable(nodes); + createTable(mesh); } std::size_t size() const @@ -51,22 +46,27 @@ public: return _data[node_id].size(); } - std::vector const& getAdjacentNodes(std::size_t const node_id) const + std::vector const& getAdjacentNodes( + std::size_t const node_id) const { return _data[node_id]; } - void createTable(std::vector const& nodes) + void createTable(Mesh const& mesh) { + auto const& nodes = mesh.getNodes(); if (_data.size() != nodes.size()) { _data.resize(nodes.size()); } - for (auto n_ptr : nodes) + auto const& connections = + MeshLib::calculateNodesConnectedByElements(mesh); + for (auto const* node : nodes) { - std::vector const& connected_nodes = n_ptr->getConnectedNodes(); - std::vector& row = _data[n_ptr->getID()]; + auto const node_id = node->getID(); + auto const& connected_nodes = connections[node_id]; + std::vector& row = _data[node_id]; row.reserve(connected_nodes.size()); std::transform(connected_nodes.cbegin(), connected_nodes.cend(), std::back_inserter(row), @@ -76,7 +76,6 @@ public: private: std::vector> _data; - }; } // namespace MeshLib diff --git a/NumLib/DOF/ComputeSparsityPattern.cpp b/NumLib/DOF/ComputeSparsityPattern.cpp index 97e802a31f..7dd41d9bd8 100644 --- a/NumLib/DOF/ComputeSparsityPattern.cpp +++ b/NumLib/DOF/ComputeSparsityPattern.cpp @@ -36,7 +36,7 @@ GlobalSparsityPattern computeSparsityPatternPETSc( GlobalSparsityPattern computeSparsityPatternNonPETSc( NumLib::LocalToGlobalIndexMap const& dof_table, MeshLib::Mesh const& mesh) { - MeshLib::NodeAdjacencyTable const node_adjacency_table(mesh.getNodes()); + MeshLib::NodeAdjacencyTable const node_adjacency_table(mesh); // A mapping mesh node id -> global indices // It acts as a cache for dof table queries. diff --git a/Tests/MeshLib/TestNodeAdjacencyTable.cpp b/Tests/MeshLib/TestNodeAdjacencyTable.cpp index c9c34f031c..984428ee63 100644 --- a/Tests/MeshLib/TestNodeAdjacencyTable.cpp +++ b/Tests/MeshLib/TestNodeAdjacencyTable.cpp @@ -24,7 +24,7 @@ TEST(MeshLib, CreateNodeAdjacencyTable1D) std::unique_ptr mesh( MeshGenerator::generateLineMesh(double(1), std::size_t(10))); - NodeAdjacencyTable table(mesh->getNodes()); + NodeAdjacencyTable table(*mesh); // There must be as many entries as there are nodes in the mesh. ASSERT_EQ(mesh->getNumberOfNodes(), table.size()); @@ -60,7 +60,7 @@ TEST(MeshLib, CreateNodeAdjacencyTable2D) std::unique_ptr mesh(MeshGenerator::generateRegularQuadMesh( 1, 1, std::size_t(10), std::size_t(10))); - NodeAdjacencyTable table(mesh->getNodes()); + NodeAdjacencyTable table(*mesh); // There must be as many entries as there are nodes in the mesh. ASSERT_EQ(mesh->getNumberOfNodes(), table.size()); @@ -99,7 +99,7 @@ TEST(MeshLib, CreateNodeAdjacencyTable3D) // double(1), double(1), double(1), std::size_t(10), std::size_t(10), // std::size_t(10))); - NodeAdjacencyTable table(mesh->getNodes()); + NodeAdjacencyTable table(*mesh); // There must be as many entries as there are nodes in the mesh. ASSERT_EQ(mesh->getNumberOfNodes(), table.size()); diff --git a/Tests/MeshLib/TestQuadraticMesh.cpp b/Tests/MeshLib/TestQuadraticMesh.cpp index 9bdfb6df72..0b53e41fed 100644 --- a/Tests/MeshLib/TestQuadraticMesh.cpp +++ b/Tests/MeshLib/TestQuadraticMesh.cpp @@ -49,17 +49,18 @@ TEST(MeshLib, QuadraticOrderMesh_Line) } } + auto const& connections = MeshLib::calculateNodesConnectedByElements(*mesh); for (MeshLib::Node const* node : mesh->getNodes()) { if (node->getID() == 1) { ASSERT_EQ(2u, node->getElements().size()); - ASSERT_EQ(5u, node->getConnectedNodes().size()); + ASSERT_EQ(5u, connections[node->getID()].size()); } else { ASSERT_EQ(1u, node->getElements().size()); - ASSERT_EQ(3u, node->getConnectedNodes().size()); + ASSERT_EQ(3u, connections[node->getID()].size()); } } } @@ -94,29 +95,36 @@ TEST(MeshLib, QuadraticOrderMesh_Quad8) } auto const& mesh_nodes = mesh->getNodes(); + auto const& connections = MeshLib::calculateNodesConnectedByElements(*mesh); // Count nodes shared by four elements and also connected to all 21 nodes. - ASSERT_EQ(1, std::count_if(mesh_nodes.begin(), mesh_nodes.end(), - [](Node* const n) { - return (n->getElements().size() == 4) && - (n->getConnectedNodes().size() == 21); - })); + ASSERT_EQ(1, + std::count_if(mesh_nodes.begin(), mesh_nodes.end(), + [&connections](Node* const n) + { + return (n->getElements().size() == 4) && + (connections[n->getID()].size() == 21); + })); // Count nodes belonging to one element and also connected to all 8 nodes of // that corner element. - ASSERT_EQ(12, std::count_if(mesh_nodes.begin(), mesh_nodes.end(), - [](Node* const n) { - return (n->getElements().size() == 1) && - (n->getConnectedNodes().size() == 8); - })); + ASSERT_EQ(12, + std::count_if(mesh_nodes.begin(), mesh_nodes.end(), + [&connections](Node* const n) + { + return (n->getElements().size() == 1) && + (connections[n->getID()].size() == 8); + })); // Count nodes shared by two elements and also connected to the 13 nodes of // the two elements. - ASSERT_EQ(8, std::count_if(mesh_nodes.begin(), mesh_nodes.end(), - [](Node* const n) { - return (n->getElements().size() == 2) && - (n->getConnectedNodes().size() == 13); - })); + ASSERT_EQ(8, + std::count_if(mesh_nodes.begin(), mesh_nodes.end(), + [&connections](Node* const n) + { + return (n->getElements().size() == 2) && + (connections[n->getID()].size() == 13); + })); } TEST(MeshLib, QuadraticOrderMesh_Quad9) @@ -150,29 +158,35 @@ TEST(MeshLib, QuadraticOrderMesh_Quad9) auto const& mesh_nodes = mesh->getNodes(); + auto const& connections = MeshLib::calculateNodesConnectedByElements(*mesh); // Count nodes shared by four elements and also connected to all 25 nodes. - ASSERT_EQ(1, std::count_if( - mesh_nodes.begin(), mesh_nodes.end(), [](Node* const n) { - return (n->getElements().size() == 4) && - (n->getConnectedNodes().size() == 21 + 4); - })); + ASSERT_EQ( + 1, std::count_if(mesh_nodes.begin(), mesh_nodes.end(), + [&connections](Node* const n) + { + return (n->getElements().size() == 4) && + (connections[n->getID()].size() == 21 + 4); + })); // Count nodes belonging to one element and also connected to all 9 nodes of // that corner element---three corners and the centre node. - ASSERT_EQ( - 12 + 4, - std::count_if(mesh_nodes.begin(), mesh_nodes.end(), [](Node* const n) { - return (n->getElements().size() == 1) && - (n->getConnectedNodes().size() == 9); - })); + ASSERT_EQ(12 + 4, + std::count_if(mesh_nodes.begin(), mesh_nodes.end(), + [&connections](Node* const n) + { + return (n->getElements().size() == 1) && + (connections[n->getID()].size() == 9); + })); // Count nodes shared by two elements and also connected to the 15 nodes of // the two elements. - ASSERT_EQ(8, std::count_if( - mesh_nodes.begin(), mesh_nodes.end(), [](Node* const n) { - return (n->getElements().size() == 2) && - (n->getConnectedNodes().size() == 13 + 2); - })); + ASSERT_EQ( + 8, std::count_if(mesh_nodes.begin(), mesh_nodes.end(), + [&connections](Node* const n) + { + return (n->getElements().size() == 2) && + (connections[n->getID()].size() == 13 + 2); + })); } TEST(MeshLib, QuadraticOrderMesh_LineQuad) @@ -240,34 +254,43 @@ TEST(MeshLib, QuadraticOrderMesh_LineQuad) auto const& mesh_nodes = mesh->getNodes(); + auto const& connections = MeshLib::calculateNodesConnectedByElements(*mesh); // Count nodes shared by six elements and also connected to all 21 nodes. - ASSERT_EQ(1, std::count_if(mesh_nodes.begin(), mesh_nodes.end(), - [](Node* const n) { - return (n->getElements().size() == 6) && - (n->getConnectedNodes().size() == 21); - })); + ASSERT_EQ(1, + std::count_if(mesh_nodes.begin(), mesh_nodes.end(), + [&connections](Node* const n) + { + return (n->getElements().size() == 6) && + (connections[n->getID()].size() == 21); + })); // Count nodes belonging to one element and also connected to all 8 nodes of // that corner element. - ASSERT_EQ(12, std::count_if(mesh_nodes.begin(), mesh_nodes.end(), - [](Node* const n) { - return (n->getElements().size() == 1) && - (n->getConnectedNodes().size() == 8); - })); + ASSERT_EQ(12, + std::count_if(mesh_nodes.begin(), mesh_nodes.end(), + [&connections](Node* const n) + { + return (n->getElements().size() == 1) && + (connections[n->getID()].size() == 8); + })); // Count nodes shared by three elements (quads and the line) and also // connected to the 13 nodes of the two elements. - ASSERT_EQ(4, std::count_if(mesh_nodes.begin(), mesh_nodes.end(), - [](Node* const n) { - return (n->getElements().size() == 3) && - (n->getConnectedNodes().size() == 13); - })); + ASSERT_EQ(4, + std::count_if(mesh_nodes.begin(), mesh_nodes.end(), + [&connections](Node* const n) + { + return (n->getElements().size() == 3) && + (connections[n->getID()].size() == 13); + })); // Count nodes shared by two elements (quads) and also connected to the 13 // nodes of the two elements. - ASSERT_EQ(4, std::count_if(mesh_nodes.begin(), mesh_nodes.end(), - [](Node* const n) { - return (n->getElements().size() == 2) && - (n->getConnectedNodes().size() == 13); - })); + ASSERT_EQ(4, + std::count_if(mesh_nodes.begin(), mesh_nodes.end(), + [&connections](Node* const n) + { + return (n->getElements().size() == 2) && + (connections[n->getID()].size() == 13); + })); } -- GitLab From 8238eca9e29403e00aa2987936fc2d26004e41cd Mon Sep 17 00:00:00 2001 From: Thomas Fischer Date: Fri, 9 Jul 2021 16:28:48 +0200 Subject: [PATCH 3/4] [MeL/Node] Rm node connection information. The connection information can be computed on the fly required. --- MeshLib/Node.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/MeshLib/Node.h b/MeshLib/Node.h index e9a8123d2c..cc25040724 100644 --- a/MeshLib/Node.h +++ b/MeshLib/Node.h @@ -56,9 +56,6 @@ public: /// Copy constructor Node(const Node &node); - /// Return all the nodes connected to this one - const std::vector& getConnectedNodes() const { return _connected_nodes; } - /// Get an element the node is part of. const Element* getElement(std::size_t idx) const { return _elements[idx]; } @@ -82,14 +79,6 @@ protected: /// clear stored elements connecting to this node void clearElements() { _elements.clear(); } - /// Resets the connected nodes of this node. The connected nodes are - /// generated by Mesh::setNodesConnectedByElements(). - void setConnectedNodes(std::vector const& connected_nodes) - { - _connected_nodes = connected_nodes; - } - - std::vector _connected_nodes; std::vector _elements; }; /* class */ -- GitLab From 01ec9e25fcffb303244e7a593917e965cd6a4452 Mon Sep 17 00:00:00 2001 From: Thomas Fischer Date: Thu, 16 Sep 2021 17:01:20 +0200 Subject: [PATCH 4/4] [MeL/NodePartitionedMesh] Use fct. calculateNodesConnectedByElements. --- MeshLib/NodePartitionedMesh.h | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/MeshLib/NodePartitionedMesh.h b/MeshLib/NodePartitionedMesh.h index 0f06c384cf..484b1f178d 100644 --- a/MeshLib/NodePartitionedMesh.h +++ b/MeshLib/NodePartitionedMesh.h @@ -40,23 +40,9 @@ public: _n_active_nodes(mesh.getNumberOfNodes()), _is_single_thread(true) { - const auto& mesh_nodes = mesh.getNodes(); for (std::size_t i = 0; i < _nodes.size(); i++) { _global_node_ids[i] = _nodes[i]->getID(); - - // TODO To add copying of the connected nodes (and elements) - // in the copy constructor of class Node in order to - // drop the following lines. - auto node = _nodes[i]; - // Copy constructor of Mesh does not copy the connected - // nodes to node. - if (node->_connected_nodes.size() == 0) - { - std::copy(mesh_nodes[i]->_connected_nodes.begin(), - mesh_nodes[i]->_connected_nodes.end(), - std::back_inserter(node->_connected_nodes)); - } } } @@ -141,14 +127,15 @@ public: /// Get the maximum number of connected nodes to node. std::size_t getMaximumNConnectedNodesToNode() const { - std::vector::const_iterator it_max_ncn = std::max_element( - _nodes.cbegin(), _nodes.cend(), - [](Node const* const node_a, Node const* const node_b) { - return (node_a->getConnectedNodes().size() < - node_b->getConnectedNodes().size()); + auto const& nodes_connections = + MeshLib::calculateNodesConnectedByElements(*this); + auto const max_connections = std::max_element( + nodes_connections.cbegin(), nodes_connections.cend(), + [](auto const& connections_node_a, auto const& connections_node_b) { + return (connections_node_a.size() < connections_node_b.size()); }); // Return the number of connected nodes +1 for the node itself. - return (*it_max_ncn)->getConnectedNodes().size() + 1; + return max_connections->size() + 1; } bool isForSingleThread() const { return _is_single_thread; } -- GitLab