void TestGenerateVectorsOfElementsStraddlingPeriodicBoundaries() throw (Exception) { TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/bad_cylindrical_9_1"); Cylindrical2dMesh mesh(9.1); mesh.ConstructFromMeshReader(mesh_reader); // We now emulate the commands of the ReMesh function as far as it goes before generating the lists { mesh.CreateHaloNodes(); mesh.CreateMirrorNodes(); NodeMap big_map(mesh.GetNumAllNodes()); mesh.MutableMesh<2,2>::ReMesh(big_map); } mesh.GenerateVectorsOfElementsStraddlingPeriodicBoundaries(); TS_ASSERT_EQUALS(mesh.mLeftPeriodicBoundaryElementIndices.size(), 43u); // The commented test below fails as there are no nodes waiting to be deleted // and the current mesh is Voronoi, hence no call is made to triangle... // TS_ASSERT_EQUALS(mesh.mRightPeriodicBoundaryElementIndices.size(), 42u); // Test the GetCorrespondingNodeIndex() method // ... instead, there should still be the same number TS_ASSERT_EQUALS(mesh.mRightPeriodicBoundaryElementIndices.size(), 43u); TS_ASSERT_EQUALS(mesh.GetCorrespondingNodeIndex(393), 84u); TS_ASSERT_EQUALS(mesh.GetCorrespondingNodeIndex(84), 393u); TS_ASSERT_EQUALS(mesh.GetCorrespondingNodeIndex(188), 329u); TS_ASSERT_EQUALS(mesh.GetCorrespondingNodeIndex(329), 188u); mesh.CorrectNonPeriodicMesh(); mesh.DeleteHaloNodes(); }
void TestCorrectNonPeriodicMeshes() throw (Exception) { std::vector<Node<2>*> nodes; // Generates a mesh which could be meshed in different ways. nodes.push_back(new Node<2>(0, true, 1.1, 0.0)); nodes.push_back(new Node<2>(1, true, 3.0, 0.0)); nodes.push_back(new Node<2>(2, true, 1.1, 2.0)); // Stabilise mesh and prevent extra edge elements nodes.push_back(new Node<2>(3, true, 2.9, 2.0)); nodes.push_back(new Node<2>(4, true, 1.0, 4.0)); nodes.push_back(new Node<2>(5, true, 3.0, 4.0)); const double width = 4.0; Cylindrical2dMesh mesh(width, nodes); // Create the mirrored nodes - double the size of the mesh mesh.CreateMirrorNodes(); // Create elements for the new larger mesh NodeMap big_map(mesh.GetNumAllNodes()); mesh.MutableMesh<2,2>::ReMesh(big_map); // We need the mesh in a certain configuration for this test TS_ASSERT_EQUALS(mesh.GetNumElements(), 12u); mesh.GenerateVectorsOfElementsStraddlingPeriodicBoundaries(); std::set<unsigned> left_elements = mesh.mLeftPeriodicBoundaryElementIndices; std::set<unsigned> right_elements = mesh.mRightPeriodicBoundaryElementIndices; // There should be four elements on each side which cross the periodic boundary TS_ASSERT_EQUALS(left_elements.size(), 4u); TS_ASSERT_EQUALS(right_elements.size(), 4u); /* * Swap one of the pairs of elements on the left around so that * CorrectNonPeriodicMesh() has some work to do. * Note this test could possibly make the mesh break the Voronoi condition, * but this is OK as the CorrectNonPeriodicMesh() * deals with cases where the Voronoi definition is ambiguous. */ // A pair of elements on the left are flipped around. mesh.GetElement(0)->UpdateNode(0, mesh.GetNode(0)); mesh.GetElement(1)->UpdateNode(1, mesh.GetNode(10)); mesh.CorrectNonPeriodicMesh(); // Check that these elements have been swapped around. // Note that the element indices are changed by each call and you // have to reexamine the mesh if changing this test TS_ASSERT_EQUALS(mesh.GetElement(10)->GetNode(0)->GetIndex(), 2u); TS_ASSERT_EQUALS(mesh.GetElement(10)->GetNode(1)->GetIndex(), 10u); TS_ASSERT_EQUALS(mesh.GetElement(10)->GetNode(2)->GetIndex(), 9u); TS_ASSERT_EQUALS(mesh.GetElement(11)->GetNode(0)->GetIndex(), 2u); TS_ASSERT_EQUALS(mesh.GetElement(11)->GetNode(1)->GetIndex(), 9u); TS_ASSERT_EQUALS(mesh.GetElement(11)->GetNode(2)->GetIndex(), 0u); // A pair of elements on the right are flipped around. mesh.GetElement(4)->UpdateNode(0, mesh.GetNode(6)); mesh.GetElement(6)->UpdateNode(1, mesh.GetNode(3)); mesh.CorrectNonPeriodicMesh(); // Check that these elements have been swapped around. // Note that the element indices are changed by each call and you // have to reexamine the mesh if changing this test TS_ASSERT_EQUALS(mesh.GetElement(10)->GetNode(0)->GetIndex(), 0u); TS_ASSERT_EQUALS(mesh.GetElement(10)->GetNode(1)->GetIndex(), 10u); TS_ASSERT_EQUALS(mesh.GetElement(10)->GetNode(2)->GetIndex(), 9u); TS_ASSERT_EQUALS(mesh.GetElement(11)->GetNode(0)->GetIndex(), 2u); TS_ASSERT_EQUALS(mesh.GetElement(11)->GetNode(1)->GetIndex(), 10u); TS_ASSERT_EQUALS(mesh.GetElement(11)->GetNode(2)->GetIndex(), 0u); // We can now reconstruct the cylindrical mesh without any problems TS_ASSERT_THROWS_NOTHING(mesh.ReconstructCylindricalMesh()); }
void Cylindrical2dMesh::ReMesh(NodeMap& rMap) { unsigned old_num_all_nodes = GetNumAllNodes(); rMap.Resize(old_num_all_nodes); rMap.ResetToIdentity(); // Flag the deleted nodes as deleted in the map for (unsigned i=0; i<old_num_all_nodes; i++) { if (mNodes[i]->IsDeleted()) { rMap.SetDeleted(i); } } CreateHaloNodes(); // Create mirrored nodes for the normal remesher to work with CreateMirrorNodes(); /* * The mesh now has messed up boundary elements, but this * doesn't matter as the ReMesh() below doesn't read them in * and reconstructs the boundary elements. * * Call ReMesh() on the parent class. Note that the mesh now has lots * of extra nodes which will be deleted, hence the name 'big_map'. */ NodeMap big_map(GetNumAllNodes()); MutableMesh<2,2>::ReMesh(big_map); /* * If the big_map isn't the identity map, the little map ('map') needs to be * altered accordingly before being passed to the user. Not sure how this all * works, so deal with this bridge when we get to it. */ assert(big_map.IsIdentityMap()); // Re-index the vectors according to the big nodemap, and set up the maps. mImageToLeftOriginalNodeMap.clear(); mImageToRightOriginalNodeMap.clear(); assert(mLeftOriginals.size() == mLeftImages.size()); assert(mRightOriginals.size() == mRightImages.size()); for (unsigned i=0; i<mLeftOriginals.size(); i++) { mLeftOriginals[i] = big_map.GetNewIndex(mLeftOriginals[i]); mLeftImages[i] = big_map.GetNewIndex(mLeftImages[i]); mImageToLeftOriginalNodeMap[mLeftImages[i]] = mLeftOriginals[i]; } for (unsigned i=0; i<mRightOriginals.size(); i++) { mRightOriginals[i] = big_map.GetNewIndex(mRightOriginals[i]); mRightImages[i] = big_map.GetNewIndex(mRightImages[i]); mImageToRightOriginalNodeMap[mRightImages[i]] = mRightOriginals[i]; } for (unsigned i=0; i<mTopHaloNodes.size(); i++) { mTopHaloNodes[i] = big_map.GetNewIndex(mTopHaloNodes[i]); mBottomHaloNodes[i] = big_map.GetNewIndex(mBottomHaloNodes[i]); } /* * Check that elements crossing the periodic boundary have been meshed * in the same way at each side. */ CorrectNonPeriodicMesh(); /* * Take the double-sized mesh, with its new boundary elements, and * remove the relevant nodes, elements and boundary elements to leave * a proper periodic mesh. */ ReconstructCylindricalMesh(); DeleteHaloNodes(); /* * Create a random boundary element between two nodes of the first * element if it is not deleted. This is a temporary measure to get * around re-index crashing when there are no boundary elements. */ unsigned num_elements = GetNumAllElements(); bool boundary_element_made = false; unsigned elem_index = 0; while (elem_index<num_elements && !boundary_element_made) { Element<2,2>* p_element = GetElement(elem_index); if (!p_element->IsDeleted()) { boundary_element_made = true; std::vector<Node<2>*> nodes; nodes.push_back(p_element->GetNode(0)); nodes.push_back(p_element->GetNode(1)); BoundaryElement<1,2>* p_boundary_element = new BoundaryElement<1,2>(0, nodes); p_boundary_element->RegisterWithNodes(); mBoundaryElements.push_back(p_boundary_element); this->mBoundaryElementWeightedDirections.push_back(zero_vector<double>(2)); this->mBoundaryElementJacobianDeterminants.push_back(0.0); } elem_index++; } // Now call ReIndex() to remove the temporary nodes which are marked as deleted NodeMap reindex_map(GetNumAllNodes()); ReIndex(reindex_map); assert(!reindex_map.IsIdentityMap()); // maybe don't need this /* * Go through the reindex map and use it to populate the original NodeMap * (the one that is returned to the user). */ for (unsigned i=0; i<rMap.GetSize(); i++) // only going up to be size of map, not size of reindex_map { if (reindex_map.IsDeleted(i)) { /* * i < num_original_nodes and node is deleted, this should correspond to * a node that was labelled as before the remeshing, so should have already * been set as deleted in the map above. */ assert(rMap.IsDeleted(i)); } else { rMap.SetNewIndex(i, reindex_map.GetNewIndex(i)); } } // We can now clear the index vectors and maps; they are only used for remeshing mLeftOriginals.clear(); mLeftImages.clear(); mImageToLeftOriginalNodeMap.clear(); mRightOriginals.clear(); mRightImages.clear(); mImageToRightOriginalNodeMap.clear(); mLeftPeriodicBoundaryElementIndices.clear(); mRightPeriodicBoundaryElementIndices.clear(); }