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
00033
00034 #define REAL double
00035 #define VOID void
00036 #include "triangle.h"
00037 #undef REAL
00038 #undef VOID
00039
00040 template<unsigned DIM>
00041 QuadraticMesh<DIM>::QuadraticMesh(const std::string& rFileName, bool boundaryElemFileIsQuadratic)
00042 {
00043 LoadFromFile(rFileName, boundaryElemFileIsQuadratic);
00044
00045
00046 #ifndef NDEBUG
00047 unsigned expected_num_nodes = DIM*(DIM+1)/2;
00048 for (typename TetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00049 = this->GetBoundaryElementIteratorBegin();
00050 iter != this->GetBoundaryElementIteratorEnd();
00051 ++iter)
00052 {
00053 assert((*iter)->GetNumNodes()==expected_num_nodes);
00054 }
00055 #endif
00056 }
00057
00058
00059 template<unsigned DIM>
00060 QuadraticMesh<DIM>::QuadraticMesh(double xEnd, double yEnd, unsigned numElemX, unsigned numElemY)
00061 {
00062 assert(DIM==2);
00063
00064 assert(xEnd>0);
00065 assert(yEnd>0);
00066 assert(numElemX>0);
00067 assert(numElemY>0);
00068
00069 unsigned num_nodes=(numElemX+1)*(numElemY+1);
00070 struct triangulateio triangle_input;
00071 triangle_input.pointlist = (double *) malloc( num_nodes * 2 * sizeof(double));
00072 triangle_input.numberofpoints = num_nodes;
00073 triangle_input.numberofpointattributes = 0;
00074 triangle_input.pointmarkerlist = NULL;
00075 triangle_input.numberofsegments = 0;
00076 triangle_input.numberofholes = 0;
00077 triangle_input.numberofregions = 0;
00078
00079 unsigned new_index = 0;
00080 for (unsigned j=0; j<=numElemY; j++)
00081 {
00082 double y = yEnd*j/numElemY;
00083 for (unsigned i=0; i<=numElemX; i++)
00084 {
00085 double x = xEnd*i/numElemX;
00086
00087 triangle_input.pointlist[2*new_index] = x;
00088 triangle_input.pointlist[2*new_index + 1] = y;
00089 new_index++;
00090 }
00091 }
00092
00093
00094 struct triangulateio triangle_output;
00095 triangle_output.pointlist = NULL;
00096 triangle_output.pointattributelist = (double *) NULL;
00097 triangle_output.pointmarkerlist = (int *) NULL;
00098 triangle_output.trianglelist = (int *) NULL;
00099 triangle_output.triangleattributelist = (double *) NULL;
00100 triangle_output.edgelist = (int *) NULL;
00101 triangle_output.edgemarkerlist = (int *) NULL;
00102
00103
00104 triangulate((char*)"Qzeo2", &triangle_input, &triangle_output, NULL);
00105
00106 assert(triangle_output.numberofcorners == 6);
00107
00108
00109 for (unsigned node_index=0; node_index<(unsigned)triangle_output.numberofpoints; node_index++)
00110 {
00111 if (triangle_output.pointmarkerlist[node_index] == 1)
00112 {
00113
00114 Node<DIM> *p_node = new Node<DIM>(node_index, true,
00115 triangle_output.pointlist[node_index * 2],
00116 triangle_output.pointlist[node_index * 2+1]);
00117 this->mNodes.push_back(p_node);
00118 this->mBoundaryNodes.push_back(p_node);
00119 }
00120 else
00121 {
00122 this->mNodes.push_back(new Node<DIM>(node_index, false,
00123 triangle_output.pointlist[node_index * 2],
00124 triangle_output.pointlist[node_index * 2+1]));
00125 }
00126 }
00127
00128 mIsInternalNode.resize(this->GetNumNodes(), true);
00129
00130
00131 this->mElements.reserve(triangle_output.numberoftriangles);
00132 for (unsigned element_index=0; element_index<(unsigned)triangle_output.numberoftriangles; element_index++)
00133 {
00134 std::vector<Node<DIM>*> nodes;
00135
00136 for (unsigned j=0; j<3; j++)
00137 {
00138 unsigned global_node_index = triangle_output.trianglelist[element_index*6 + j];
00139 assert(global_node_index < this->mNodes.size());
00140 nodes.push_back(this->mNodes[global_node_index]);
00141 mIsInternalNode[global_node_index]=false;
00142 }
00143
00144 this->mElements.push_back(new Element<DIM, DIM>(element_index, nodes));
00145
00146 for (unsigned j=3; j<6; j++)
00147 {
00148 unsigned global_node_index = triangle_output.trianglelist[element_index*6 + j];
00149 assert(global_node_index < this->mNodes.size());
00150 this->mElements[element_index]->AddNode( this->mNodes[global_node_index] );
00151 this->mNodes[j]->AddElement(element_index);
00152 }
00153 }
00154 bool vertices_mode = true;
00155 mNumVertices = 0u;
00156 for (unsigned i=0; i<this->GetNumNodes(); i++)
00157 {
00158 if (mIsInternalNode[i]==false)
00159 {
00160 mNumVertices++;
00161 assert(vertices_mode);
00162 }
00163 if ( (vertices_mode == true) && (mIsInternalNode[i]==true) )
00164 {
00165 vertices_mode = false;
00166 }
00167 }
00168 unsigned next_boundary_element_index = 0;
00169 for (unsigned boundary_element_index=0; boundary_element_index<(unsigned)triangle_output.numberofedges; boundary_element_index++)
00170 {
00171 if (triangle_output.edgemarkerlist[boundary_element_index] == 1)
00172 {
00173 std::vector<Node<DIM>*> nodes;
00174 for (unsigned j=0; j<2; j++)
00175 {
00176 unsigned global_node_index=triangle_output.edgelist[boundary_element_index*2 + j];
00177 assert(global_node_index < this->mNodes.size());
00178 nodes.push_back(this->mNodes[global_node_index]);
00179 }
00180 this->mBoundaryElements.push_back(new BoundaryElement<DIM-1, DIM>(next_boundary_element_index++, nodes));
00181 }
00182 }
00183
00184 this->RefreshJacobianCachedData();
00185
00186 AddNodesToBoundaryElements();
00187
00188 free(triangle_input.pointlist);
00189
00190 free(triangle_output.pointlist);
00191 free(triangle_output.pointattributelist);
00192 free(triangle_output.pointmarkerlist);
00193 free(triangle_output.trianglelist);
00194 free(triangle_output.triangleattributelist);
00195 free(triangle_output.edgelist);
00196 free(triangle_output.edgemarkerlist);
00197 }
00198
00199
00200 template<unsigned DIM>
00201 QuadraticMesh<DIM>::QuadraticMesh(double xEnd, double yEnd, double zEnd,
00202 unsigned numElemX, unsigned numElemY, unsigned numElemZ)
00203 {
00204 assert(DIM==3);
00205
00206 assert(xEnd>0);
00207 assert(yEnd>0);
00208 assert(zEnd>0);
00209 assert(numElemX>0);
00210 assert(numElemY>0);
00211 assert(numElemZ>0);
00212
00213 std::string tempfile_name_stem = "temp_quadmesh3d";
00214
00216
00218 OutputFileHandler handler("");
00219 out_stream p_file = handler.OpenOutputFile(tempfile_name_stem+".node");
00220
00221 *p_file << (numElemX+1)*(numElemY+1)*(numElemZ+1) << " 3 0 0\n";
00222 unsigned node_index = 0;
00223 for (unsigned k=0; k<=numElemZ; k++)
00224 {
00225 for (unsigned j=0; j<=numElemY; j++)
00226 {
00227 for (unsigned i=0; i<=numElemX; i++)
00228 {
00229 double x = xEnd*i/numElemX;
00230 double y = yEnd*j/numElemY;
00231 double z = zEnd*k/numElemZ;
00232
00233
00234 *p_file << node_index++ << " " << x << " " << y << " " << z << "\n";
00235 }
00236 }
00237 }
00238 p_file->close();
00239
00241
00243
00244
00245 RunMesherAndReadMesh("tetgen", handler.GetOutputDirectoryFullPath(), tempfile_name_stem);
00246 }
00247
00248
00249 template<unsigned DIM>
00250 unsigned QuadraticMesh<DIM>::GetNumVertices()
00251 {
00252 return mNumVertices;
00253 }
00254
00255
00256 template<unsigned DIM>
00257 void QuadraticMesh<DIM>::RunMesherAndReadMesh(std::string binary,
00258 std::string outputDir,
00259 std::string fileStem)
00260 {
00261
00262 assert(DIM == 3);
00263 std::string args = "-Qo2";
00264
00265
00266 std::string command = binary + " " + args + " " + outputDir
00267 + "/" + fileStem + ".node" + " > /dev/null";
00268
00269 int return_value = system(command.c_str());
00270
00271 if (return_value != 0)
00272 {
00273 #define COVERAGE_IGNORE
00274 EXCEPTION("Remeshing (by calling " + binary + ") failed. Do you have it in your path?\n"+
00275 "The quadratic mesh relies on functionality from tetgen (http://tetgen.berlios.de/).");
00276 #undef COVERAGE_IGNORE
00277 }
00278
00279
00280 command = "mv " + outputDir + "/"
00281 + fileStem + ".1.* .";
00282
00283
00284
00285
00286 return_value = system(command.c_str());
00287
00288
00289 LoadFromFile( fileStem + ".1", false);
00290
00291
00292 command = "rm -f " + outputDir + "/" + fileStem + ".node";
00293 EXPECT0(system, command);
00294 EXPECT0(system, "rm -f " + fileStem + ".1.node");
00295 EXPECT0(system, "rm -f " + fileStem + ".1.ele");
00296 EXPECT0(system, "rm -f " + fileStem + ".1.face");
00297 }
00298
00299
00300 template<unsigned DIM>
00301 void QuadraticMesh<DIM>::LoadFromFile(const std::string& rFileName, bool boundaryElemFileIsQuadratic)
00302 {
00303 unsigned order_of_boundary_elements = boundaryElemFileIsQuadratic ? 2 : 1;
00304
00305 TrianglesMeshReader<DIM,DIM> mesh_reader(rFileName, 2, order_of_boundary_elements);
00306
00307 ConstructFromMeshReader(mesh_reader);
00308
00309
00310
00311 mIsInternalNode.resize(this->GetNumNodes(), true);
00312 for (unsigned elem_index=0; elem_index<this->GetNumElements(); elem_index++)
00313 {
00314 for (unsigned i=0; i<DIM+1 ; i++)
00315 {
00316 unsigned node_index = this->GetElement(elem_index)->GetNodeGlobalIndex(i);
00317 mIsInternalNode[ node_index ] = false;
00318 }
00319 }
00320
00321
00322
00323 mNumVertices = 0;
00324 bool vertices_mode = true;
00325 for (unsigned i=0; i<this->GetNumNodes(); i++)
00326 {
00327 if (mIsInternalNode[i]==false)
00328 {
00329 mNumVertices++;
00330 }
00331 if ((vertices_mode == false) && (mIsInternalNode[i]==false ) )
00332 {
00333
00334 EXCEPTION("The quadratic mesh doesn't appear to have all vertices before the rest of the nodes");
00335 }
00336 if ( (vertices_mode == true) && (mIsInternalNode[i]==true) )
00337 {
00338 vertices_mode = false;
00339 }
00340 }
00341
00342 mesh_reader.Reset();
00343
00344
00345
00346 for (unsigned i=0; i<this->GetNumElements(); i++)
00347 {
00348 std::vector<unsigned> nodes = mesh_reader.GetNextElementData().NodeIndices;
00349 assert(nodes.size()==(DIM+1)*(DIM+2)/2);
00350 assert(this->GetElement(i)->GetNumNodes()==DIM+1);
00351
00352 for (unsigned j=DIM+1; j<(DIM+1)*(DIM+2)/2; j++)
00353 {
00354 this->GetElement(i)->AddNode( this->GetNode(nodes[j]) );
00355 this->GetNode(nodes[j])->AddElement(this->GetElement(i)->GetIndex());
00356 }
00357 }
00358
00359 if (DIM > 1)
00360 {
00361
00362 if (boundaryElemFileIsQuadratic)
00363 {
00364 mesh_reader.Reset();
00365 for (typename TetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00366 = this->GetBoundaryElementIteratorBegin();
00367 iter != this->GetBoundaryElementIteratorEnd();
00368 ++iter)
00369 {
00370 std::vector<unsigned> nodes = mesh_reader.GetNextFaceData().NodeIndices;
00371
00372 assert((*iter)->GetNumNodes()==DIM);
00373 assert(nodes.size()==DIM*(DIM+1)/2);
00374
00375 for (unsigned j=DIM; j<DIM*(DIM+1)/2; j++)
00376 {
00377 (*iter)->AddNode( this->GetNode(nodes[j]) );
00378 }
00379 }
00380 }
00381 else
00382 {
00383 AddNodesToBoundaryElements();
00384 }
00385 }
00386 }
00387
00388 template<unsigned DIM>
00389 void QuadraticMesh<DIM>::AddNodesToBoundaryElements()
00390 {
00391
00392
00393 if (DIM>1)
00394 {
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406 for (typename TetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00407 = this->GetBoundaryElementIteratorBegin();
00408 iter != this->GetBoundaryElementIteratorEnd();
00409 ++iter)
00410 {
00411
00412
00413
00414 std::set<unsigned> boundary_element_node_indices;
00415 for (unsigned i=0; i<DIM; i++)
00416 {
00417 boundary_element_node_indices.insert( (*iter)->GetNodeGlobalIndex(i) );
00418 }
00419
00420 bool found_this_boundary_element = false;
00421
00422
00423 for (unsigned i=0; i<this->GetNumElements(); i++)
00424 {
00425 Element<DIM,DIM> *p_element = this->GetElement(i);
00426
00427
00428 for (unsigned face=0; face<DIM+1; face++)
00429 {
00430
00431 std::set<unsigned> node_indices;
00432 for (unsigned local_node_index=0; local_node_index<DIM+1; local_node_index++)
00433 {
00434 if (local_node_index!=face)
00435 {
00436 node_indices.insert( p_element->GetNodeGlobalIndex(local_node_index) );
00437 }
00438 }
00439
00440 assert(node_indices.size()==DIM);
00441
00442
00443
00444 if (node_indices==boundary_element_node_indices)
00445 {
00446 AddExtraBoundaryNodes(*iter, p_element, face);
00447
00448 found_this_boundary_element = true;
00449 break;
00450 }
00451 }
00452
00453 if (found_this_boundary_element)
00454 {
00455 break;
00456 }
00457 }
00458
00459 if (!found_this_boundary_element)
00460 {
00461 #define COVERAGE_IGNORE
00462 EXCEPTION("Unable to find a face of an element which matches one of the boundary elements");
00463 #undef COVERAGE_IGNORE
00464 }
00465 }
00466 }
00467 }
00468
00469
00470 template<unsigned DIM>
00471 void QuadraticMesh<DIM>::AddNodeToBoundaryElement(BoundaryElement<DIM-1,DIM>* pBoundaryElement,
00472 Element<DIM,DIM>* pElement,
00473 unsigned internalNode)
00474 {
00475 assert(DIM>1);
00476 assert(internalNode >= DIM+1);
00477 assert(internalNode < (DIM+1)*(DIM+2)/2);
00478 Node<DIM> *p_internal_node = pElement->GetNode(internalNode);
00479
00480
00481 if (!p_internal_node->IsBoundaryNode())
00482 {
00483 p_internal_node->SetAsBoundaryNode();
00484 this->mBoundaryNodes.push_back(p_internal_node);
00485 }
00486
00487 pBoundaryElement->AddNode( p_internal_node );
00488 }
00489
00490
00491 template<unsigned DIM>
00492 void QuadraticMesh<DIM>::AddExtraBoundaryNodes(BoundaryElement<DIM-1,DIM>* pBoundaryElement,
00493 Element<DIM,DIM>* pElement,
00494 unsigned nodeIndexOppositeToFace)
00495 {
00496 assert(DIM!=1);
00497 if (DIM==2)
00498 {
00499 assert(nodeIndexOppositeToFace<3);
00500
00501 AddNodeToBoundaryElement(pBoundaryElement, pElement, nodeIndexOppositeToFace+3);
00502 }
00503 else
00504 {
00505 assert(DIM==3);
00506
00507 unsigned b_elem_n0 = pBoundaryElement->GetNodeGlobalIndex(0);
00508 unsigned b_elem_n1 = pBoundaryElement->GetNodeGlobalIndex(1);
00509
00510 unsigned offset;
00511 bool reverse;
00512
00513 if (nodeIndexOppositeToFace==0)
00514 {
00515
00516 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 1, 2, 3, offset, reverse);
00517 HelperMethod2(pBoundaryElement, pElement, 9, 8, 5, offset, reverse);
00518 }
00519 else if (nodeIndexOppositeToFace==1)
00520 {
00521
00522 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 2, 0, 3, offset, reverse);
00523 HelperMethod2(pBoundaryElement, pElement, 7, 9, 6, offset, reverse);
00524 }
00525 else if (nodeIndexOppositeToFace==2)
00526 {
00527
00528 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 0, 1, 3, offset, reverse);
00529 HelperMethod2(pBoundaryElement, pElement, 8, 7, 4, offset, reverse);
00530 }
00531 else
00532 {
00533 assert(nodeIndexOppositeToFace==3);
00534
00535 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 0, 1, 2, offset, reverse);
00536 HelperMethod2(pBoundaryElement, pElement, 5, 6, 4, offset, reverse);
00537 }
00538 }
00539 }
00540
00541 template<unsigned DIM>
00542 void QuadraticMesh<DIM>::WriteBoundaryElementFile(std::string directory, std::string fileName)
00543 {
00544 OutputFileHandler handler(directory, false);
00545 out_stream p_file = handler.OpenOutputFile(fileName);
00546
00547 unsigned expected_num_nodes;
00548 assert(DIM > 1);
00549 if (DIM == 2)
00550 {
00551 expected_num_nodes = 3;
00552 }
00553 else if (DIM == 3)
00554 {
00555 expected_num_nodes = 6;
00556 }
00557
00558 unsigned num_elements = 0;
00559
00560 for (typename TetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00561 = this->GetBoundaryElementIteratorBegin();
00562 iter != this->GetBoundaryElementIteratorEnd();
00563 ++iter)
00564 {
00565 assert((*iter)->GetNumNodes()==expected_num_nodes);
00566 num_elements++;
00567 }
00568
00569 *p_file << num_elements << " 0\n";
00570
00571 unsigned counter = 0;
00572 for (typename TetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00573 = this->GetBoundaryElementIteratorBegin();
00574 iter != this->GetBoundaryElementIteratorEnd();
00575 ++iter)
00576 {
00577 *p_file << counter++ << " ";
00578 for (unsigned i=0; i<(*iter)->GetNumNodes(); i++)
00579 {
00580 *p_file << (*iter)->GetNodeGlobalIndex(i) << " ";
00581 }
00582 *p_file << "\n";
00583 }
00584
00585 p_file->close();
00586 }
00587
00589
00591
00592 #define COVERAGE_IGNORE
00593 template<unsigned DIM>
00594 void QuadraticMesh<DIM>::HelperMethod1(unsigned boundaryElemNode0, unsigned boundaryElemNode1,
00595 Element<DIM,DIM>* pElement,
00596 unsigned node0, unsigned node1, unsigned node2,
00597 unsigned& rOffset,
00598 bool& rReverse)
00599 {
00600 if (pElement->GetNodeGlobalIndex(node0)==boundaryElemNode0)
00601 {
00602 rOffset = 0;
00603 if (pElement->GetNodeGlobalIndex(node1)==boundaryElemNode1)
00604 {
00605 rReverse = false;
00606 }
00607 else
00608 {
00609 assert(pElement->GetNodeGlobalIndex(node2)==boundaryElemNode1);
00610 rReverse = true;
00611 }
00612 }
00613 else if (pElement->GetNodeGlobalIndex(node1)==boundaryElemNode0)
00614 {
00615 rOffset = 1;
00616 if (pElement->GetNodeGlobalIndex(node2)==boundaryElemNode1)
00617 {
00618 rReverse = false;
00619 }
00620 else
00621 {
00622 assert(pElement->GetNodeGlobalIndex(node0)==boundaryElemNode1);
00623 rReverse = true;
00624 }
00625 }
00626 else
00627 {
00628 assert(pElement->GetNodeGlobalIndex(node2)==boundaryElemNode0);
00629 rOffset = 2;
00630 if (pElement->GetNodeGlobalIndex(node0)==boundaryElemNode1)
00631 {
00632 rReverse = false;
00633 }
00634 else
00635 {
00636 assert(pElement->GetNodeGlobalIndex(node1)==boundaryElemNode1);
00637 rReverse = true;
00638 }
00639 }
00640 }
00641 #undef COVERAGE_IGNORE
00642
00643
00644 #define COVERAGE_IGNORE
00645 template<unsigned DIM>
00646 void QuadraticMesh<DIM>::HelperMethod2(BoundaryElement<DIM-1,DIM>* pBoundaryElement,
00647 Element<DIM,DIM>* pElement,
00648 unsigned internalNode0, unsigned internalNode1, unsigned internalNode2,
00649 unsigned offset,
00650 bool reverse)
00651 {
00652 if (offset==1)
00653 {
00654 unsigned temp = internalNode0;
00655 internalNode0 = internalNode1;
00656 internalNode1 = internalNode2;
00657 internalNode2 = temp;
00658 }
00659 else if (offset == 2)
00660 {
00661 unsigned temp = internalNode0;
00662 internalNode0 = internalNode2;
00663 internalNode2 = internalNode1;
00664 internalNode1 = temp;
00665 }
00666
00667 if (reverse)
00668 {
00669 unsigned temp = internalNode1;
00670 internalNode1 = internalNode2;
00671 internalNode2 = temp;
00672 }
00673
00674 AddNodeToBoundaryElement(pBoundaryElement, pElement, internalNode0);
00675 AddNodeToBoundaryElement(pBoundaryElement, pElement, internalNode1);
00676 AddNodeToBoundaryElement(pBoundaryElement, pElement, internalNode2);
00677 }
00678 #undef COVERAGE_IGNORE
00679
00680
00682
00684
00685
00686 template class QuadraticMesh<1>;
00687 template class QuadraticMesh<2>;
00688 template class QuadraticMesh<3>;