diff --git a/Applications/Utils/MeshEdit/moveMeshNodes.cpp b/Applications/Utils/MeshEdit/moveMeshNodes.cpp index 33e13bdf7acbec74420c2705365b701828a0ed9a..d7c4a9c6cdc90613e20ce63dc3b443729b27d7e5 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 e844ce81809c2397de7f5b0e8f8eb1bb5d778b4a..1ebd40fde92f319e471a3a6aae1698061c3f4291 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/Mesh.cpp b/MeshLib/Mesh.cpp index 6cf57cec18e821e52cd118a3d3f4ecaf3b2e4e08..9e81ba0cee548beb3bfdb5b51a9754ecd02866bb 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 2056afbe4da47a237df3195a92b3a233c3d26606..0cca934b120ab2bd56cc25e34491163046e219e5 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) diff --git a/MeshLib/Node.h b/MeshLib/Node.h index e9a8123d2ca21d6a5e3b05a7869ed34fbd993e50..cc2504072421dfdfd607a2fc493fe3d9e8ba9943 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 */ diff --git a/MeshLib/NodeAdjacencyTable.h b/MeshLib/NodeAdjacencyTable.h index da9f1fcc4a6789add542ffd78310e27d347ece15..507630db932dcf293a0378c2c8d066f25699aab4 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/MeshLib/NodePartitionedMesh.h b/MeshLib/NodePartitionedMesh.h index 0f06c384cfb29c64d5d2039eeefac8130244713d..484b1f178d58d378e779fb3930b0e1014ae47777 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; } diff --git a/NumLib/DOF/ComputeSparsityPattern.cpp b/NumLib/DOF/ComputeSparsityPattern.cpp index 97e802a31fe03d55ad96cfe54902bf1d21ae5bb5..7dd41d9bd82034ed81905af1deb43e4d459fcadc 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 c9c34f031cd3d2b9298deac4e0cd6ea86e59580b..984428ee6342830f1ad38213ee078e462a919da5 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 9bdfb6df7216e5cf13ef9efe01d5898f1129d239..0b53e41fedceddc4c6f9bbcf238466d6c0173dc2 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); + })); }