00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "QuadraticMesh.hpp"
00030 #include "OutputFileHandler.hpp"
00031 #include "TrianglesMeshReader.hpp"
00032 #include "Warnings.hpp"
00033
00034
00035 #define REAL double
00036 #define VOID void
00037 #include "triangle.h"
00038 #include "tetgen.h"
00039 #undef REAL
00040 #undef VOID
00041
00042 template<unsigned DIM>
00043 void QuadraticMesh<DIM>::CountAndCheckVertices()
00044 {
00045
00046
00047
00048
00049 mNumVertices = 0;
00050 for (unsigned i=0; i<this->GetNumNodes(); i++)
00051 {
00052 bool is_internal = this->GetNode(i)->IsInternal();
00053 if (is_internal==false)
00054 {
00055 mNumVertices++;
00056 }
00057 }
00058 }
00059
00060 template<unsigned DIM>
00061 QuadraticMesh<DIM>::QuadraticMesh(double spaceStep, double width, double height, double depth)
00062 {
00063 this->ConstructRegularSlabMesh(spaceStep, width, height, depth);
00064 }
00065
00066 template<unsigned DIM>
00067 void QuadraticMesh<DIM>::ConstructLinearMesh(unsigned numElemX)
00068 {
00069 this->mNodes.resize(2*numElemX+1);
00070 mNumVertices = numElemX+1;
00071
00072
00073 Node<DIM>* p_edge_node = new Node<DIM>(0, true, 0.0);
00074 this->mNodes[0] = p_edge_node;
00075 this->mBoundaryNodes.push_back(p_edge_node);
00076 this->mBoundaryElements.push_back(new BoundaryElement<DIM-1,DIM>(0, p_edge_node) );
00077
00078 for (unsigned element_index=0; element_index<numElemX; element_index++)
00079 {
00080 unsigned right_node_index = element_index+1;
00081 unsigned mid_node_index = mNumVertices + element_index;
00082
00083 double x_value_right_x = right_node_index;
00084 double x_value_mid_node = x_value_right_x-0.5;
00085
00086 bool is_boundary = (element_index+1==numElemX);
00087 Node<DIM>* p_right_node = new Node<DIM>(right_node_index, is_boundary, x_value_right_x);
00088 Node<DIM>* p_mid_node = new Node<DIM>(mid_node_index, false, x_value_mid_node);
00089
00090 this->mNodes[right_node_index] = p_right_node;
00091 this->mNodes[mid_node_index] = p_mid_node;
00092
00093 if (element_index+1==numElemX)
00094 {
00095 this->mBoundaryNodes.push_back(p_right_node);
00096 this->mBoundaryElements.push_back(new BoundaryElement<DIM-1,DIM>(1, p_right_node) );
00097 }
00098
00099 std::vector<Node<DIM>*> nodes;
00100 nodes.push_back(this->mNodes[right_node_index-1]);
00101 nodes.push_back(this->mNodes[right_node_index]);
00102 nodes.push_back(this->mNodes[mid_node_index]);
00103 this->mElements.push_back(new Element<DIM,DIM>(element_index, nodes) );
00104 }
00105
00106 this->RefreshMesh();
00107 }
00108
00109
00110
00111
00112 template<unsigned DIM>
00113 void QuadraticMesh<DIM>::ConstructRectangularMesh(unsigned numElemX, unsigned numElemY, bool unused)
00114 {
00115 assert(DIM==2);
00116
00117 assert(numElemX > 0);
00118 assert(numElemY > 0);
00119 assert(unused);
00120
00121 this->mMeshIsLinear=false;
00122 unsigned num_nodes=(numElemX+1)*(numElemY+1);
00123 struct triangulateio mesher_input;
00124
00125 this->InitialiseTriangulateIo(mesher_input);
00126 mesher_input.pointlist = (double *) malloc( num_nodes * DIM * sizeof(double));
00127 mesher_input.numberofpoints = num_nodes;
00128
00129 unsigned new_index = 0;
00130 for (unsigned j=0; j<=numElemY; j++)
00131 {
00132 double y = j;
00133 for (unsigned i=0; i<=numElemX; i++)
00134 {
00135 double x = i;
00136
00137 mesher_input.pointlist[DIM*new_index] = x;
00138 mesher_input.pointlist[DIM*new_index + 1] = y;
00139 new_index++;
00140 }
00141 }
00142
00143
00144 struct triangulateio mesher_output;
00145
00146 this->InitialiseTriangulateIo(mesher_output);
00147
00148
00149 triangulate((char*)"Qzeo2", &mesher_input, &mesher_output, NULL);
00150
00151 assert(mesher_output.numberofcorners == (DIM+1)*(DIM+2)/2);
00152
00153 this->ImportFromMesher(mesher_output, mesher_output.numberoftriangles, mesher_output.trianglelist, mesher_output.numberofedges, mesher_output.edgelist, mesher_output.edgemarkerlist);
00154
00155 CountAndCheckVertices();
00156 AddNodesToBoundaryElements(NULL);
00157
00158 this->FreeTriangulateIo(mesher_input);
00159 this->FreeTriangulateIo(mesher_output);
00160 }
00161
00162
00163
00164 template<unsigned DIM>
00165 void QuadraticMesh<DIM>::ConstructCuboid(unsigned numElemX, unsigned numElemY, unsigned numElemZ)
00166 {
00167 assert(DIM==3);
00168
00169 assert(numElemX > 0);
00170 assert(numElemY > 0);
00171 assert(numElemZ > 0);
00172 this->mMeshIsLinear = false;
00173 unsigned num_nodes = (numElemX+1)*(numElemY+1)*(numElemZ+1);
00174
00175 struct tetgen::tetgenio mesher_input;
00176 mesher_input.pointlist = new double[num_nodes * DIM];
00177 mesher_input.numberofpoints = num_nodes;
00178 unsigned new_index = 0;
00179 for (unsigned k=0; k<=numElemZ; k++)
00180 {
00181 double z = k;
00182 for (unsigned j=0; j<=numElemY; j++)
00183 {
00184 double y = j;
00185 for (unsigned i=0; i<=numElemX; i++)
00186 {
00187 double x = i;
00188 mesher_input.pointlist[DIM*new_index] = x;
00189 mesher_input.pointlist[DIM*new_index + 1] = y;
00190 mesher_input.pointlist[DIM*new_index + 2] = z;
00191 new_index++;
00192 }
00193 }
00194 }
00195
00196
00197 struct tetgen::tetgenio mesher_output;
00198 tetgen::tetrahedralize((char*)"Qo2", &mesher_input, &mesher_output, NULL);
00199
00200 assert(mesher_output.numberofcorners == (DIM+1)*(DIM+2)/2);
00201
00202 this->ImportFromMesher(mesher_output, mesher_output.numberoftetrahedra, mesher_output.tetrahedronlist, mesher_output.numberoftrifaces, mesher_output.trifacelist, NULL);
00203
00204 CountAndCheckVertices();
00205 AddNodesToBoundaryElements(NULL);
00206 }
00207
00208
00209 template<unsigned DIM>
00210 unsigned QuadraticMesh<DIM>::GetNumVertices()
00211 {
00212 return mNumVertices;
00213 }
00214
00215
00216 template<unsigned DIM>
00217 void QuadraticMesh<DIM>::ConstructFromLinearMeshReader(AbstractMeshReader<DIM, DIM>& rMeshReader)
00218 {
00219 assert(DIM != 1);
00220
00221
00222 TetrahedralMesh<DIM,DIM>::ConstructFromMeshReader(rMeshReader);
00223
00224 NodeMap unused_map(this->GetNumNodes());
00225
00226 if (DIM==2)
00227 {
00228 struct triangulateio mesher_input, mesher_output;
00229 this->InitialiseTriangulateIo(mesher_input);
00230 this->InitialiseTriangulateIo(mesher_output);
00231
00232 mesher_input.numberoftriangles = this->GetNumElements();
00233 mesher_input.trianglelist = (int *) malloc(this->GetNumElements() * (DIM+1) * sizeof(int));
00234 this->ExportToMesher(unused_map, mesher_input, mesher_input.trianglelist);
00235
00236
00237 triangulate((char*)"Qzero2", &mesher_input, &mesher_output, NULL);
00238
00239 this->ImportFromMesher(mesher_output, mesher_output.numberoftriangles, mesher_output.trianglelist, mesher_output.numberofedges, mesher_output.edgelist, mesher_output.edgemarkerlist);
00240 CountAndCheckVertices();
00241 AddNodesToBoundaryElements(NULL);
00242
00243
00244 this->FreeTriangulateIo(mesher_input);
00245 this->FreeTriangulateIo(mesher_output);
00246 }
00247 else
00248 {
00249
00250 struct tetgen::tetgenio mesher_input, mesher_output;
00251
00252 mesher_input.numberoftetrahedra = this->GetNumElements();
00253 mesher_input.tetrahedronlist = new int[this->GetNumElements() * (DIM+1)];
00254 this->ExportToMesher(unused_map, mesher_input, mesher_input.tetrahedronlist);
00255
00256
00257 tetgen::tetrahedralize((char*)"Qzro2", &mesher_input, &mesher_output);
00258
00259 this->ImportFromMesher(mesher_output, mesher_output.numberoftetrahedra, mesher_output.tetrahedronlist, mesher_output.numberoftrifaces, mesher_output.trifacelist, NULL);
00260 CountAndCheckVertices();
00261 AddNodesToBoundaryElements(NULL);
00262 }
00263 }
00264
00265
00266 template<unsigned DIM>
00267 void QuadraticMesh<DIM>::ConstructFromMeshReader(AbstractMeshReader<DIM, DIM>& rAbsMeshReader)
00268 {
00269 TrianglesMeshReader<DIM, DIM>* p_mesh_reader=dynamic_cast<TrianglesMeshReader<DIM, DIM>*>(&rAbsMeshReader);
00270
00271 unsigned order_of_elements = 1;
00272 if (p_mesh_reader)
00273 {
00274
00275 order_of_elements = p_mesh_reader->GetOrderOfElements();
00276 }
00277
00278
00279 if (order_of_elements == 1)
00280 {
00281 WARNING("Reading a (linear) tetrahedral mesh and converting it to a QuadraticMesh. This involves making an external library call to Triangle/Tetgen in order to compute in internal nodes");
00282 ConstructFromLinearMeshReader(rAbsMeshReader);
00283 return;
00284 }
00285
00286 TetrahedralMesh<DIM,DIM>::ConstructFromMeshReader(*p_mesh_reader);
00287 assert(this->GetNumBoundaryElements() > 0);
00288
00289 p_mesh_reader->Reset();
00290
00291
00292 for (unsigned i=0; i<this->GetNumElements(); i++)
00293 {
00294 std::vector<unsigned> nodes = p_mesh_reader->GetNextElementData().NodeIndices;
00295 assert(nodes.size()==(DIM+1)*(DIM+2)/2);
00296 assert(this->GetElement(i)->GetNumNodes()==DIM+1);
00297
00298
00299 for (unsigned j=DIM+1; j<(DIM+1)*(DIM+2)/2; j++)
00300 {
00301 this->GetElement(i)->AddNode( this->GetNode(nodes[j]) );
00302 this->GetNode(nodes[j])->AddElement(this->GetElement(i)->GetIndex());
00303 this->GetNode(nodes[j])->MarkAsInternal();
00304 }
00305 }
00306
00307 CountAndCheckVertices();
00308
00309 if (DIM > 1)
00310 {
00311
00312 if (p_mesh_reader->GetOrderOfBoundaryElements() == 2u)
00313 {
00314 p_mesh_reader->Reset();
00315 for (typename TetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00316 = this->GetBoundaryElementIteratorBegin();
00317 iter != this->GetBoundaryElementIteratorEnd();
00318 ++iter)
00319 {
00320 std::vector<unsigned> nodes = p_mesh_reader->GetNextFaceData().NodeIndices;
00321
00322 assert((*iter)->GetNumNodes()==DIM);
00323 assert(nodes.size()==DIM*(DIM+1)/2);
00324
00325 for (unsigned j=DIM; j<DIM*(DIM+1)/2; j++)
00326 {
00327 Node <DIM> *p_internal_node = this->GetNode(nodes[j]);
00328 (*iter)->AddNode( p_internal_node );
00329
00330 if (!p_internal_node->IsBoundaryNode())
00331 {
00332 p_internal_node->SetAsBoundaryNode();
00333 this->mBoundaryNodes.push_back(p_internal_node);
00334 }
00335 }
00336 }
00337 }
00338 else
00339 {
00340 AddNodesToBoundaryElements(p_mesh_reader);
00341 }
00342 }
00343
00344
00345 #ifndef NDEBUG
00346 unsigned expected_num_nodes = DIM*(DIM+1)/2;
00347 for (typename TetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00348 = this->GetBoundaryElementIteratorBegin();
00349 iter != this->GetBoundaryElementIteratorEnd();
00350 ++iter)
00351 {
00352 assert((*iter)->GetNumNodes()==expected_num_nodes);
00353 }
00354 #endif
00355 }
00356
00357 template<unsigned DIM>
00358 void QuadraticMesh<DIM>::AddNodesToBoundaryElements(TrianglesMeshReader<DIM,DIM>* pMeshReader)
00359 {
00360
00361
00362 bool boundary_element_file_has_containing_element_info=false;
00363
00364 if (pMeshReader)
00365 {
00366 boundary_element_file_has_containing_element_info=pMeshReader->GetReadContainingElementOfBoundaryElement();
00367 }
00368
00369 if (DIM > 1)
00370 {
00371 if (boundary_element_file_has_containing_element_info)
00372 {
00373 pMeshReader->Reset();
00374 }
00375
00376 for (typename TetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00377 = this->GetBoundaryElementIteratorBegin();
00378 iter != this->GetBoundaryElementIteratorEnd();
00379 ++iter)
00380 {
00381
00382
00383 std::set<unsigned> boundary_element_node_indices;
00384 for (unsigned i=0; i<DIM; i++)
00385 {
00386 boundary_element_node_indices.insert( (*iter)->GetNodeGlobalIndex(i) );
00387 }
00388
00389 bool found_this_boundary_element = false;
00390
00391
00392
00393
00394
00395 Node<DIM>* p_representative_node = (*iter)->GetNode(0);
00396 for (typename Node<DIM>::ContainingElementIterator element_iter = p_representative_node->ContainingElementsBegin();
00397 element_iter != p_representative_node->ContainingElementsEnd();
00398 ++element_iter)
00399 {
00400 unsigned elem_index = *element_iter;
00401
00402
00403 if (boundary_element_file_has_containing_element_info)
00404 {
00405 elem_index = pMeshReader->GetNextFaceData().ContainingElement;
00406 }
00407
00408 Element<DIM,DIM>* p_element = this->GetElement(elem_index);
00409
00410
00411 for (unsigned face=0; face<DIM+1; face++)
00412 {
00413
00414 std::set<unsigned> node_indices;
00415 for (unsigned local_node_index=0; local_node_index<DIM+1; local_node_index++)
00416 {
00417 if (local_node_index!=face)
00418 {
00419 node_indices.insert( p_element->GetNodeGlobalIndex(local_node_index) );
00420 }
00421 }
00422
00423 assert(node_indices.size()==DIM);
00424
00425
00426
00427 if (node_indices==boundary_element_node_indices)
00428 {
00429 AddExtraBoundaryNodes(*iter, p_element, face);
00430
00431 found_this_boundary_element = true;
00432 break;
00433 }
00434 }
00435
00436
00437 if (boundary_element_file_has_containing_element_info && !found_this_boundary_element)
00438 {
00439 #define COVERAGE_IGNORE
00440
00441 EXCEPTION("Boundary element " << (*iter)->GetIndex()
00442 << "wasn't found in the containing element given for it "
00443 << elem_index);
00444 #undef COVERAGE_IGNORE
00445 }
00446
00447 if (found_this_boundary_element)
00448 {
00449 break;
00450 }
00451 }
00452
00453 if (!found_this_boundary_element)
00454 {
00455 #define COVERAGE_IGNORE
00456 EXCEPTION("Unable to find a face of an element which matches one of the boundary elements");
00457 #undef COVERAGE_IGNORE
00458 }
00459 }
00460 }
00461 }
00462
00463
00464 template<unsigned DIM>
00465 void QuadraticMesh<DIM>::AddNodeToBoundaryElement(BoundaryElement<DIM-1,DIM>* pBoundaryElement,
00466 Element<DIM,DIM>* pElement,
00467 unsigned internalNode)
00468 {
00469 assert(DIM > 1);
00470 assert(internalNode >= DIM+1);
00471 assert(internalNode < (DIM+1)*(DIM+2)/2);
00472 Node<DIM>* p_internal_node = pElement->GetNode(internalNode);
00473
00474
00475 if (!p_internal_node->IsBoundaryNode())
00476 {
00477 p_internal_node->SetAsBoundaryNode();
00478 this->mBoundaryNodes.push_back(p_internal_node);
00479 }
00480
00481 pBoundaryElement->AddNode( p_internal_node );
00482 }
00483
00484
00485 template<unsigned DIM>
00486 void QuadraticMesh<DIM>::AddExtraBoundaryNodes(BoundaryElement<DIM-1,DIM>* pBoundaryElement,
00487 Element<DIM,DIM>* pElement,
00488 unsigned nodeIndexOppositeToFace)
00489 {
00490 assert(DIM!=1);
00491 if (DIM==2)
00492 {
00493 assert(nodeIndexOppositeToFace<3);
00494
00495 AddNodeToBoundaryElement(pBoundaryElement, pElement, nodeIndexOppositeToFace+3);
00496 }
00497 else
00498 {
00499 assert(DIM==3);
00500
00501 unsigned b_elem_n0 = pBoundaryElement->GetNodeGlobalIndex(0);
00502 unsigned b_elem_n1 = pBoundaryElement->GetNodeGlobalIndex(1);
00503
00504 unsigned offset;
00505 bool reverse;
00506
00507 if (nodeIndexOppositeToFace==0)
00508 {
00509
00510 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 1, 2, 3, offset, reverse);
00511 HelperMethod2(pBoundaryElement, pElement, 9, 8, 5, offset, reverse);
00512 }
00513 else if (nodeIndexOppositeToFace==1)
00514 {
00515
00516 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 2, 0, 3, offset, reverse);
00517 HelperMethod2(pBoundaryElement, pElement, 7, 9, 6, offset, reverse);
00518 }
00519 else if (nodeIndexOppositeToFace==2)
00520 {
00521
00522 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 0, 1, 3, offset, reverse);
00523 HelperMethod2(pBoundaryElement, pElement, 8, 7, 4, offset, reverse);
00524 }
00525 else
00526 {
00527 assert(nodeIndexOppositeToFace==3);
00528
00529 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 0, 1, 2, offset, reverse);
00530 HelperMethod2(pBoundaryElement, pElement, 5, 6, 4, offset, reverse);
00531 }
00532 }
00533 }
00534
00535 template<unsigned DIM>
00536 void QuadraticMesh<DIM>::WriteBoundaryElementFile(std::string directory, std::string fileName)
00537 {
00538 OutputFileHandler handler(directory, false);
00539 out_stream p_file = handler.OpenOutputFile(fileName);
00540
00541 unsigned expected_num_nodes;
00542 assert(DIM > 1);
00543 if (DIM == 2)
00544 {
00545 expected_num_nodes = 3;
00546 }
00547 else if (DIM == 3)
00548 {
00549 expected_num_nodes = 6;
00550 }
00551
00552 unsigned num_elements = 0;
00553
00554 for (typename TetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00555 = this->GetBoundaryElementIteratorBegin();
00556 iter != this->GetBoundaryElementIteratorEnd();
00557 ++iter)
00558 {
00559 assert((*iter)->GetNumNodes()==expected_num_nodes);
00560 num_elements++;
00561 }
00562
00563 *p_file << num_elements << " 0\n";
00564
00565 unsigned counter = 0;
00566 for (typename TetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00567 = this->GetBoundaryElementIteratorBegin();
00568 iter != this->GetBoundaryElementIteratorEnd();
00569 ++iter)
00570 {
00571 *p_file << counter++ << " ";
00572 for (unsigned i=0; i<(*iter)->GetNumNodes(); i++)
00573 {
00574 *p_file << (*iter)->GetNodeGlobalIndex(i) << " ";
00575 }
00576 *p_file << "\n";
00577 }
00578
00579 p_file->close();
00580 }
00581
00583
00585
00586 #define COVERAGE_IGNORE /// \todo These helper methods aren't properly covered
00587 template<unsigned DIM>
00588 void QuadraticMesh<DIM>::HelperMethod1(unsigned boundaryElemNode0, unsigned boundaryElemNode1,
00589 Element<DIM,DIM>* pElement,
00590 unsigned node0, unsigned node1, unsigned node2,
00591 unsigned& rOffset,
00592 bool& rReverse)
00593 {
00594 if (pElement->GetNodeGlobalIndex(node0)==boundaryElemNode0)
00595 {
00596 rOffset = 0;
00597 if (pElement->GetNodeGlobalIndex(node1)==boundaryElemNode1)
00598 {
00599 rReverse = false;
00600 }
00601 else
00602 {
00603 assert(pElement->GetNodeGlobalIndex(node2)==boundaryElemNode1);
00604 rReverse = true;
00605 }
00606 }
00607 else if (pElement->GetNodeGlobalIndex(node1)==boundaryElemNode0)
00608 {
00609 rOffset = 1;
00610 if (pElement->GetNodeGlobalIndex(node2)==boundaryElemNode1)
00611 {
00612 rReverse = false;
00613 }
00614 else
00615 {
00616 assert(pElement->GetNodeGlobalIndex(node0)==boundaryElemNode1);
00617 rReverse = true;
00618 }
00619 }
00620 else
00621 {
00622 assert(pElement->GetNodeGlobalIndex(node2)==boundaryElemNode0);
00623 rOffset = 2;
00624 if (pElement->GetNodeGlobalIndex(node0)==boundaryElemNode1)
00625 {
00626 rReverse = false;
00627 }
00628 else
00629 {
00630 assert(pElement->GetNodeGlobalIndex(node1)==boundaryElemNode1);
00631 rReverse = true;
00632 }
00633 }
00634 }
00635 #undef COVERAGE_IGNORE /// \todo These helper methods aren't properly covered
00636
00637
00638 #define COVERAGE_IGNORE /// \todo These helper methods aren't properly covered
00639 template<unsigned DIM>
00640 void QuadraticMesh<DIM>::HelperMethod2(BoundaryElement<DIM-1,DIM>* pBoundaryElement,
00641 Element<DIM,DIM>* pElement,
00642 unsigned internalNode0, unsigned internalNode1, unsigned internalNode2,
00643 unsigned offset,
00644 bool reverse)
00645 {
00646 if (offset==1)
00647 {
00648 unsigned temp = internalNode0;
00649 internalNode0 = internalNode1;
00650 internalNode1 = internalNode2;
00651 internalNode2 = temp;
00652 }
00653 else if (offset == 2)
00654 {
00655 unsigned temp = internalNode0;
00656 internalNode0 = internalNode2;
00657 internalNode2 = internalNode1;
00658 internalNode1 = temp;
00659 }
00660
00661 if (reverse)
00662 {
00663 unsigned temp = internalNode1;
00664 internalNode1 = internalNode2;
00665 internalNode2 = temp;
00666 }
00667
00668 AddNodeToBoundaryElement(pBoundaryElement, pElement, internalNode0);
00669 AddNodeToBoundaryElement(pBoundaryElement, pElement, internalNode1);
00670 AddNodeToBoundaryElement(pBoundaryElement, pElement, internalNode2);
00671 }
00672 #undef COVERAGE_IGNORE /// \todo These helper methods aren't properly covered
00673
00674
00676
00678
00679
00680 template class QuadraticMesh<1>;
00681 template class QuadraticMesh<2>;
00682 template class QuadraticMesh<3>;
00683
00684
00685
00686 #include "SerializationExportWrapperForCpp.hpp"
00687 EXPORT_TEMPLATE_CLASS_SAME_DIMS(QuadraticMesh)