Chaste Release::3.1
|
00001 /* 00002 00003 Copyright (c) 2005-2012, University of Oxford. 00004 All rights reserved. 00005 00006 University of Oxford means the Chancellor, Masters and Scholars of the 00007 University of Oxford, having an administrative office at Wellington 00008 Square, Oxford OX1 2JD, UK. 00009 00010 This file is part of Chaste. 00011 00012 Redistribution and use in source and binary forms, with or without 00013 modification, are permitted provided that the following conditions are met: 00014 * Redistributions of source code must retain the above copyright notice, 00015 this list of conditions and the following disclaimer. 00016 * Redistributions in binary form must reproduce the above copyright notice, 00017 this list of conditions and the following disclaimer in the documentation 00018 and/or other materials provided with the distribution. 00019 * Neither the name of the University of Oxford nor the names of its 00020 contributors may be used to endorse or promote products derived from this 00021 software without specific prior written permission. 00022 00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00024 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00025 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00026 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00033 00034 */ 00035 00036 #include "TetrahedralMesh.hpp" 00037 00038 #include <iostream> 00039 #include <cassert> 00040 #include <sstream> 00041 #include <map> 00042 00043 #include "BoundaryElement.hpp" 00044 #include "Element.hpp" 00045 #include "Exception.hpp" 00046 #include "Node.hpp" 00047 #include "OutputFileHandler.hpp" 00048 #include "PetscTools.hpp" 00049 #include "RandomNumberGenerator.hpp" 00050 #include "Warnings.hpp" 00051 00052 // Jonathan Shewchuk's triangle and Hang Si's tetgen 00053 #define REAL double 00054 #define VOID void 00055 #include "triangle.h" 00056 #include "tetgen.h" 00057 #undef REAL 00058 #undef VOID 00059 00060 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00061 TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::TetrahedralMesh() 00062 { 00063 Clear(); 00064 } 00065 00066 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00067 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ConstructFromMeshReader( 00068 AbstractMeshReader<ELEMENT_DIM, SPACE_DIM>& rMeshReader) 00069 { 00070 this->mMeshFileBaseName = rMeshReader.GetMeshFileBaseName(); 00071 00072 // Record number of corner nodes 00073 unsigned num_nodes = rMeshReader.GetNumNodes(); 00074 00075 /* 00076 * Reserve memory for nodes, so we don't have problems with 00077 * pointers stored in elements becoming invalid. 00078 */ 00079 this->mNodes.reserve(num_nodes); 00080 00081 rMeshReader.Reset(); 00082 00083 //typename std::map<std::pair<unsigned,unsigned>,unsigned>::const_iterator iterator; 00084 //std::map<std::pair<unsigned,unsigned>,unsigned> internal_nodes_map; 00085 00086 // Add nodes 00087 std::vector<double> coords; 00088 for (unsigned i=0; i < num_nodes; i++) 00089 { 00090 coords = rMeshReader.GetNextNode(); 00091 Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(i, coords, false); 00092 00093 for (unsigned i=0; i<rMeshReader.GetNodeAttributes().size(); i++) 00094 { 00095 double attribute = rMeshReader.GetNodeAttributes()[i]; 00096 p_node->AddNodeAttribute(attribute); 00097 } 00098 00099 this->mNodes.push_back(p_node); 00100 } 00101 00102 //unsigned new_node_index = mNumCornerNodes; 00103 00104 rMeshReader.Reset(); 00105 // Add elements 00106 //new_node_index = mNumCornerNodes; 00107 this->mElements.reserve(rMeshReader.GetNumElements()); 00108 00109 for (unsigned element_index=0; element_index < (unsigned) rMeshReader.GetNumElements(); element_index++) 00110 { 00111 ElementData element_data = rMeshReader.GetNextElementData(); 00112 std::vector<Node<SPACE_DIM>*> nodes; 00113 00114 /* 00115 * NOTE: currently just reading element vertices from mesh reader - even if it 00116 * does contain information about internal nodes (ie for quadratics) this is 00117 * ignored here and used elsewhere: ie don't do this: 00118 * unsigned nodes_size = node_indices.size(); 00119 */ 00120 for (unsigned j=0; j<ELEMENT_DIM+1; j++) // num vertices=ELEMENT_DIM+1, may not be equal to nodes_size. 00121 { 00122 assert(element_data.NodeIndices[j] < this->mNodes.size()); 00123 nodes.push_back(this->mNodes[element_data.NodeIndices[j]]); 00124 } 00125 00126 Element<ELEMENT_DIM,SPACE_DIM>* p_element = new Element<ELEMENT_DIM,SPACE_DIM>(element_index, nodes); 00127 this->mElements.push_back(p_element); 00128 00129 if (rMeshReader.GetNumElementAttributes() > 0) 00130 { 00131 assert(rMeshReader.GetNumElementAttributes() == 1); 00132 double attribute_value = element_data.AttributeValue; 00133 p_element->SetAttribute(attribute_value); 00134 } 00135 } 00136 00137 // Add boundary elements and nodes 00138 for (unsigned face_index=0; face_index<(unsigned)rMeshReader.GetNumFaces(); face_index++) 00139 { 00140 ElementData face_data = rMeshReader.GetNextFaceData(); 00141 std::vector<unsigned> node_indices = face_data.NodeIndices; 00142 00143 /* 00144 * NOTE: unlike the above where we just read element *vertices* from mesh reader, here we are 00145 * going to read a quadratic mesh with internal elements. 00146 * (There are only a few meshes with internals in the face file that we might as well use them.) 00147 * 00148 */ 00149 std::vector<Node<SPACE_DIM>*> nodes; 00150 for (unsigned node_index=0; node_index<node_indices.size(); node_index++) 00151 { 00152 assert(node_indices[node_index] < this->mNodes.size()); 00153 // Add Node pointer to list for creating an element 00154 nodes.push_back(this->mNodes[node_indices[node_index]]); 00155 } 00156 00157 // This is a boundary face, so ensure all its nodes are marked as boundary nodes 00158 for (unsigned j=0; j<nodes.size(); j++) 00159 { 00160 if (!nodes[j]->IsBoundaryNode()) 00161 { 00162 nodes[j]->SetAsBoundaryNode(); 00163 this->mBoundaryNodes.push_back(nodes[j]); 00164 } 00165 00166 // Register the index that this bounday element will have with the node 00167 nodes[j]->AddBoundaryElement(face_index); 00168 } 00169 00170 // The added elements will be deleted in our destructor 00171 BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>* p_boundary_element = new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(face_index, nodes); 00172 this->mBoundaryElements.push_back(p_boundary_element); 00173 00174 if (rMeshReader.GetNumFaceAttributes() > 0) 00175 { 00176 assert(rMeshReader.GetNumFaceAttributes() == 1); 00177 double attribute_value = face_data.AttributeValue; 00178 p_boundary_element->SetAttribute(attribute_value); 00179 } 00180 } 00181 00182 RefreshJacobianCachedData(); 00183 rMeshReader.Reset(); 00184 } 00185 00186 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00187 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ReadNodesPerProcessorFile(const std::string& rNodesPerProcessorFile) 00188 { 00189 std::vector<unsigned> nodes_per_processor_vec; 00190 00191 std::ifstream file_stream(rNodesPerProcessorFile.c_str()); 00192 if (file_stream.is_open()) 00193 { 00194 while (file_stream) 00195 { 00196 unsigned nodes_per_processor; 00197 file_stream >> nodes_per_processor; 00198 00199 if (file_stream) 00200 { 00201 nodes_per_processor_vec.push_back(nodes_per_processor); 00202 } 00203 } 00204 } 00205 else 00206 { 00207 EXCEPTION("Unable to read nodes per processor file " + rNodesPerProcessorFile); 00208 } 00209 00210 unsigned sum = 0; 00211 for (unsigned i=0; i<nodes_per_processor_vec.size(); i++) 00212 { 00213 sum += nodes_per_processor_vec[i]; 00214 } 00215 00216 if (sum != this->GetNumNodes()) 00217 { 00218 EXCEPTION("Sum of nodes per processor, " << sum 00219 << ", not equal to number of nodes in mesh, " << this->GetNumNodes()); 00220 } 00221 00222 unsigned num_owned=nodes_per_processor_vec[PetscTools::GetMyRank()]; 00223 00224 if (nodes_per_processor_vec.size() != PetscTools::GetNumProcs()) 00225 { 00226 EXCEPTION("Number of processes doesn't match the size of the nodes-per-processor file"); 00227 } 00228 delete this->mpDistributedVectorFactory; 00229 this->mpDistributedVectorFactory = new DistributedVectorFactory(this->GetNumNodes(), num_owned); 00230 } 00231 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00232 bool TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::CheckIsConforming() 00233 { 00234 /* 00235 * Each face of each element is a set of node indices. 00236 * We form a set of these in order to get their parity: 00237 * all faces which appear once are inserted into the set; 00238 * all faces which appear twice are inserted and then removed from the set; 00239 * we're assuming that faces never appear more than twice. 00240 */ 00241 std::set< std::set<unsigned> > odd_parity_faces; 00242 00243 for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin(); 00244 iter != this->GetElementIteratorEnd(); 00245 ++iter) 00246 { 00247 for (unsigned face_index=0; face_index<=ELEMENT_DIM; face_index++) 00248 { 00249 std::set<unsigned> face_info; 00250 for (unsigned node_index=0; node_index<=ELEMENT_DIM; node_index++) 00251 { 00252 // Leave one index out each time 00253 if (node_index != face_index) 00254 { 00255 face_info.insert(iter->GetNodeGlobalIndex(node_index)); 00256 } 00257 } 00258 // Face is now formed - attempt to find it 00259 std::set< std::set<unsigned> >::iterator find_face=odd_parity_faces.find(face_info); 00260 if (find_face != odd_parity_faces.end()) 00261 { 00262 // Face was in set, so it now has even parity. 00263 // Remove it via the iterator 00264 odd_parity_faces.erase(find_face); 00265 } 00266 else 00267 { 00268 // Face is not in set so it now has odd parity. Insert it 00269 odd_parity_faces.insert(face_info); 00270 } 00271 00272 } 00273 } 00274 00275 /* 00276 * At this point the odd parity faces should be the same as the 00277 * boundary elements. We could check this explicitly or we could 00278 * just count them. 00279 */ 00280 return(odd_parity_faces.size() == this->GetNumBoundaryElements()); 00281 } 00282 00283 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00284 double TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetVolume() 00285 { 00286 double mesh_volume = 0.0; 00287 00288 for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin(); 00289 iter != this->GetElementIteratorEnd(); 00290 ++iter) 00291 { 00292 mesh_volume += iter->GetVolume(mElementJacobianDeterminants[iter->GetIndex()]); 00293 } 00294 00295 return mesh_volume; 00296 } 00297 00298 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00299 double TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetSurfaceArea() 00300 { 00301 // ELEMENT_DIM-1 is the dimension of the boundary element 00302 assert(ELEMENT_DIM >= 1); 00303 const unsigned bound_element_dim = ELEMENT_DIM-1; 00304 assert(bound_element_dim < 3); 00305 if ( bound_element_dim == 0) 00306 { 00307 return 0.0; 00308 } 00309 00310 double mesh_surface = 0.0; 00311 typename TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::BoundaryElementIterator it = this->GetBoundaryElementIteratorBegin(); 00312 00313 while (it != this->GetBoundaryElementIteratorEnd()) 00314 { 00315 mesh_surface += mBoundaryElementJacobianDeterminants[(*it)->GetIndex()]; 00316 it++; 00317 } 00318 00319 if ( bound_element_dim == 2) 00320 { 00321 mesh_surface /= 2.0; 00322 } 00323 00324 return mesh_surface; 00325 } 00326 00327 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00328 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::PermuteNodes() 00329 { 00330 // Make a permutation vector of the identity 00331 RandomNumberGenerator* p_rng = RandomNumberGenerator::Instance(); 00332 std::vector<unsigned> perm; 00333 p_rng->Shuffle(this->mNodes.size(), perm); 00334 00335 // Call the non-random version 00336 PermuteNodes(perm); 00337 } 00338 00339 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00340 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::PermuteNodes(const std::vector<unsigned>& perm) 00341 { 00342 // Let's not do this if there are any deleted nodes 00343 assert( this->GetNumAllNodes() == this->GetNumNodes()); 00344 00345 assert(perm.size() == this->mNodes.size()); 00346 00347 // Copy the node pointers 00348 std::vector< Node<SPACE_DIM>* > copy_m_nodes; 00349 copy_m_nodes.assign(this->mNodes.begin(), this->mNodes.end()); 00350 00351 for (unsigned original_index=0; original_index<this->mNodes.size(); original_index++) 00352 { 00353 assert(perm[original_index] < this->mNodes.size()); 00354 //perm[original_index] holds the new assigned index of that node 00355 this->mNodes[ perm[original_index] ] = copy_m_nodes[original_index]; 00356 } 00357 00358 // Update indices 00359 for (unsigned index=0; index<this->mNodes.size(); index++) 00360 { 00361 this->mNodes[index]->SetIndex(index); 00362 } 00363 00364 // Copy the permutation vector into the mesh 00365 this->mNodesPermutation = perm; 00366 } 00367 00368 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00369 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetContainingElementIndex(const ChastePoint<SPACE_DIM>& rTestPoint, 00370 bool strict, 00371 std::set<unsigned> testElements, 00372 bool onlyTryWithTestElements) 00373 { 00374 for (std::set<unsigned>::iterator iter=testElements.begin(); iter!=testElements.end(); iter++) 00375 { 00376 assert(*iter<this->GetNumElements()); 00377 if (this->mElements[*iter]->IncludesPoint(rTestPoint, strict)) 00378 { 00379 assert(!this->mElements[*iter]->IsDeleted()); 00380 return *iter; 00381 } 00382 } 00383 00384 if (!onlyTryWithTestElements) 00385 { 00386 for (unsigned i=0; i<this->mElements.size(); i++) 00387 { 00388 if (this->mElements[i]->IncludesPoint(rTestPoint, strict)) 00389 { 00390 assert(!this->mElements[i]->IsDeleted()); 00391 return i; 00392 } 00393 } 00394 } 00395 00396 // If it's in none of the elements, then throw 00397 std::stringstream ss; 00398 ss << "Point ["; 00399 for (unsigned j=0; (int)j<(int)SPACE_DIM-1; j++) 00400 { 00401 ss << rTestPoint[j] << ","; 00402 } 00403 ss << rTestPoint[SPACE_DIM-1] << "] is not in "; 00404 if (!onlyTryWithTestElements) 00405 { 00406 ss << "mesh - all elements tested"; 00407 } 00408 else 00409 { 00410 ss << "set of elements given"; 00411 } 00412 EXCEPTION(ss.str()); 00413 } 00414 00415 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00416 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetContainingElementIndexWithInitialGuess(const ChastePoint<SPACE_DIM>& rTestPoint, unsigned startingElementGuess, bool strict) 00417 { 00418 assert(startingElementGuess<this->GetNumElements()); 00419 00420 /* 00421 * Let m=startingElementGuess, N=num_elem-1. 00422 * We search from in this order: m, m+1, m+2, .. , N, 0, 1, .., m-1. 00423 */ 00424 unsigned i = startingElementGuess; 00425 bool reached_end = false; 00426 00427 while (!reached_end) 00428 { 00429 if (this->mElements[i]->IncludesPoint(rTestPoint, strict)) 00430 { 00431 assert(!this->mElements[i]->IsDeleted()); 00432 return i; 00433 } 00434 00435 // Increment 00436 i++; 00437 if (i==this->GetNumElements()) 00438 { 00439 i=0; 00440 } 00441 00442 // Back to the beginning yet? 00443 if (i==startingElementGuess) 00444 { 00445 reached_end = true; 00446 } 00447 } 00448 00449 // If it's in none of the elements, then throw 00450 std::stringstream ss; 00451 ss << "Point ["; 00452 for (unsigned j=0; (int)j<(int)SPACE_DIM-1; j++) 00453 { 00454 ss << rTestPoint[j] << ","; 00455 } 00456 ss << rTestPoint[SPACE_DIM-1] << "] is not in mesh - all elements tested"; 00457 EXCEPTION(ss.str()); 00458 } 00459 00460 00461 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00462 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetNearestElementIndex(const ChastePoint<SPACE_DIM>& rTestPoint) 00463 { 00464 double max_min_weight = -INFINITY; 00465 unsigned closest_index = 0; 00466 for (unsigned i=0; i<this->mElements.size(); i++) 00467 { 00468 c_vector<double, ELEMENT_DIM+1> weight=this->mElements[i]->CalculateInterpolationWeights(rTestPoint); 00469 double neg_weight_sum=0.0; 00470 for (unsigned j=0; j<=ELEMENT_DIM; j++) 00471 { 00472 if (weight[j] < 0.0) 00473 { 00474 neg_weight_sum += weight[j]; 00475 } 00476 } 00477 if (neg_weight_sum > max_min_weight) 00478 { 00479 max_min_weight = neg_weight_sum; 00480 closest_index = i; 00481 } 00482 } 00483 assert(!this->mElements[closest_index]->IsDeleted()); 00484 return closest_index; 00485 } 00486 00487 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00488 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetNearestElementIndexFromTestElements(const ChastePoint<SPACE_DIM>& rTestPoint, 00489 std::set<unsigned> testElements) 00490 { 00491 assert(testElements.size() > 0); 00492 00493 double max_min_weight = -INFINITY; 00494 unsigned closest_index = 0; 00495 for (std::set<unsigned>::iterator iter = testElements.begin(); 00496 iter != testElements.end(); 00497 iter++) 00498 { 00499 c_vector<double, ELEMENT_DIM+1> weight=this->mElements[*iter]->CalculateInterpolationWeights(rTestPoint); 00500 double neg_weight_sum=0.0; 00501 for (unsigned j=0; j<=ELEMENT_DIM; j++) 00502 { 00503 if (weight[j] < 0.0) 00504 { 00505 neg_weight_sum += weight[j]; 00506 } 00507 } 00508 if (neg_weight_sum > max_min_weight) 00509 { 00510 max_min_weight = neg_weight_sum; 00511 closest_index = *iter; 00512 } 00513 } 00514 assert(!this->mElements[closest_index]->IsDeleted()); 00515 return closest_index; 00516 } 00517 00518 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00519 std::vector<unsigned> TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetContainingElementIndices(const ChastePoint<SPACE_DIM> &rTestPoint) 00520 { 00521 std::vector<unsigned> element_indices; 00522 for (unsigned i=0; i<this->mElements.size(); i++) 00523 { 00524 if (this->mElements[i]->IncludesPoint(rTestPoint)) 00525 { 00526 assert(!this->mElements[i]->IsDeleted()); 00527 element_indices.push_back(i); 00528 } 00529 } 00530 return element_indices; 00531 } 00532 00533 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00534 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::Clear() 00535 { 00536 // Three loops, just like the destructor. note we don't delete boundary nodes. 00537 for (unsigned i=0; i<this->mBoundaryElements.size(); i++) 00538 { 00539 delete this->mBoundaryElements[i]; 00540 } 00541 for (unsigned i=0; i<this->mElements.size(); i++) 00542 { 00543 delete this->mElements[i]; 00544 } 00545 for (unsigned i=0; i<this->mNodes.size(); i++) 00546 { 00547 delete this->mNodes[i]; 00548 } 00549 00550 this->mNodes.clear(); 00551 this->mElements.clear(); 00552 this->mBoundaryElements.clear(); 00553 this->mBoundaryNodes.clear(); 00554 } 00555 00556 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00557 std::set<unsigned> TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::CalculateBoundaryOfFlaggedRegion() 00558 { 00559 // A set of nodes which lie on the face, size 3 in 2D, size 4 in 3D 00560 typedef std::set<unsigned> FaceNodes; 00561 00562 /* 00563 * Face maps to true the first time it is encountered, and false subsequent 00564 * times. Thus, faces mapping to true at the end are boundary faces. 00565 */ 00566 std::map<FaceNodes,bool> face_on_boundary; 00567 00568 // Loop over all elements 00569 for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin(); 00570 iter != this->GetElementIteratorEnd(); 00571 ++iter) 00572 { 00573 if (iter->IsFlagged()) 00574 { 00575 // To get faces, initially start with all nodes 00576 std::set<unsigned> all_nodes; 00577 for (unsigned i=0; i<ELEMENT_DIM+1; i++) 00578 { 00579 all_nodes.insert(iter->GetNodeGlobalIndex(i)); 00580 } 00581 00582 // Remove one node in turn to obtain each face 00583 for (unsigned i=0; i<ELEMENT_DIM+1; i++) 00584 { 00585 FaceNodes face_nodes = all_nodes; 00586 face_nodes.erase(iter->GetNodeGlobalIndex(i)); 00587 00588 // Search the map of faces to see if it contains this face 00589 std::map<FaceNodes,bool>::iterator it = face_on_boundary.find(face_nodes); 00590 00591 if (it == face_on_boundary.end()) 00592 { 00593 // Face not found, add and assume on boundary 00594 face_on_boundary[face_nodes]=true; 00595 } 00596 else 00597 { 00598 // Face found in map, so not on boundary 00599 it->second = false; 00600 } 00601 } 00602 } 00603 } 00604 00605 // Boundary nodes to be returned 00606 std::set<unsigned> boundary_of_flagged_region; 00607 00608 // Get all faces in the map 00609 std::map<FaceNodes,bool>::iterator it=face_on_boundary.begin(); 00610 while (it!=face_on_boundary.end()) 00611 { 00612 // If the face maps to true it is on the boundary 00613 if (it->second==true) 00614 { 00615 // Get all nodes in the face and put in set to be returned 00616 boundary_of_flagged_region.insert(it->first.begin(),it->first.end()); 00617 } 00618 it++; 00619 } 00620 00621 return boundary_of_flagged_region; 00622 } 00623 00624 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00625 double TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetAngleBetweenNodes(unsigned indexA, unsigned indexB) 00626 { 00627 assert(SPACE_DIM == 2); 00628 assert(SPACE_DIM == ELEMENT_DIM); 00629 00630 double x_difference = this->mNodes[indexB]->rGetLocation()[0] - this->mNodes[indexA]->rGetLocation()[0]; 00631 double y_difference = this->mNodes[indexB]->rGetLocation()[1] - this->mNodes[indexA]->rGetLocation()[1]; 00632 00633 if (x_difference == 0) 00634 { 00635 if (y_difference > 0) 00636 { 00637 return M_PI/2.0; 00638 } 00639 else if (y_difference < 0) 00640 { 00641 return -M_PI/2.0; 00642 } 00643 else 00644 { 00645 EXCEPTION("Tried to compute polar angle of (0,0)"); 00646 } 00647 } 00648 00649 double angle = atan2(y_difference,x_difference); 00650 return angle; 00651 } 00652 00653 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00654 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::UnflagAllElements() 00655 { 00656 for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin(); 00657 iter != this->GetElementIteratorEnd(); 00658 ++iter) 00659 { 00660 iter->Unflag(); 00661 } 00662 } 00663 00664 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00665 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::FlagElementsNotContainingNodes(std::set<unsigned> nodesList) 00666 { 00667 for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin(); 00668 iter != this->GetElementIteratorEnd(); 00669 ++iter) 00670 { 00671 bool found_node = false; 00672 00673 for (unsigned i=0; i<iter->GetNumNodes(); i++) 00674 { 00675 unsigned node_index = iter->GetNodeGlobalIndex(i); 00676 00677 std::set<unsigned>::iterator set_iter = nodesList.find(node_index); 00678 if (set_iter != nodesList.end()) 00679 { 00680 found_node = true; 00681 } 00682 } 00683 00684 if (!found_node) 00685 { 00686 iter->Flag(); 00687 } 00688 } 00689 } 00690 00692 // Edge iterator class // 00694 00695 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00696 Node<SPACE_DIM>* TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator::GetNodeA() 00697 { 00698 assert((*this) != mrMesh.EdgesEnd()); 00699 Element<ELEMENT_DIM,SPACE_DIM>* p_element = mrMesh.GetElement(mElemIndex); 00700 return p_element->GetNode(mNodeALocalIndex); 00701 } 00702 00703 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00704 Node<SPACE_DIM>* TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator::GetNodeB() 00705 { 00706 assert((*this) != mrMesh.EdgesEnd()); 00707 Element<ELEMENT_DIM,SPACE_DIM>* p_element = mrMesh.GetElement(mElemIndex); 00708 return p_element->GetNode(mNodeBLocalIndex); 00709 } 00710 00711 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00712 bool TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator::operator!=(const TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator& rOther) 00713 { 00714 return (mElemIndex != rOther.mElemIndex || 00715 mNodeALocalIndex != rOther.mNodeALocalIndex || 00716 mNodeBLocalIndex != rOther.mNodeBLocalIndex); 00717 } 00718 00719 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00720 typename TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator& TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator::operator++() 00721 { 00722 bool already_seen_this_edge; 00723 00724 unsigned num_elements = mrMesh.GetNumAllElements(); 00725 std::pair<unsigned, unsigned> current_node_pair; 00726 do 00727 { 00728 /* 00729 * Advance to the next edge in the mesh. 00730 * Node indices are incremented modulo #nodes_per_elem. 00731 */ 00732 mNodeBLocalIndex = (mNodeBLocalIndex + 1) % (ELEMENT_DIM+1); 00733 if (mNodeBLocalIndex == mNodeALocalIndex) 00734 { 00735 mNodeALocalIndex = (mNodeALocalIndex + 1) % (ELEMENT_DIM+1); 00736 mNodeBLocalIndex = (mNodeALocalIndex + 1) % (ELEMENT_DIM+1); 00737 } 00738 00739 if (mNodeALocalIndex == 0 && mNodeBLocalIndex == 1) // advance to next element... 00740 { 00741 // ...skipping deleted ones 00742 do 00743 { 00744 mElemIndex++; 00745 } 00746 while (mElemIndex!=num_elements && mrMesh.GetElement(mElemIndex)->IsDeleted()); 00747 } 00748 00749 if (mElemIndex != num_elements) 00750 { 00751 Element<ELEMENT_DIM, SPACE_DIM>* p_element = mrMesh.GetElement(mElemIndex); 00752 unsigned node_a_global_index = p_element->GetNodeGlobalIndex(mNodeALocalIndex); 00753 unsigned node_b_global_index = p_element->GetNodeGlobalIndex(mNodeBLocalIndex); 00754 if (node_b_global_index < node_a_global_index) 00755 { 00756 // Swap them over 00757 unsigned temp = node_a_global_index; 00758 node_a_global_index = node_b_global_index; 00759 node_b_global_index = temp; 00760 } 00761 00762 // Check we haven't seen it before 00763 current_node_pair = std::pair<unsigned, unsigned>(node_a_global_index, node_b_global_index); 00764 already_seen_this_edge = (mEdgesVisited.count(current_node_pair) != 0); 00765 } 00766 else 00767 { 00768 already_seen_this_edge = false; 00769 } 00770 } 00771 00772 while (already_seen_this_edge); 00773 mEdgesVisited.insert(current_node_pair); 00774 00775 return (*this); 00776 } 00777 00778 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00779 TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator::EdgeIterator(TetrahedralMesh& rMesh, unsigned elemIndex) 00780 : mrMesh(rMesh), 00781 mElemIndex(elemIndex), 00782 mNodeALocalIndex(0), 00783 mNodeBLocalIndex(1) 00784 { 00785 if (elemIndex == mrMesh.GetNumAllElements()) 00786 { 00787 return; 00788 } 00789 00790 mEdgesVisited.clear(); 00791 00792 // Add the current node pair to the store 00793 unsigned node_a_global_index = mrMesh.GetElement(mElemIndex)->GetNodeGlobalIndex(mNodeALocalIndex); 00794 unsigned node_b_global_index = mrMesh.GetElement(mElemIndex)->GetNodeGlobalIndex(mNodeBLocalIndex); 00795 if (node_b_global_index < node_a_global_index) 00796 { 00797 // Swap them over 00798 unsigned temp = node_a_global_index; 00799 node_a_global_index = node_b_global_index; 00800 node_b_global_index = temp; 00801 } 00802 00803 // Check we haven't seen it before 00804 std::pair<unsigned, unsigned> current_node_pair = std::pair<unsigned, unsigned>(node_a_global_index, node_b_global_index); 00805 mEdgesVisited.insert(current_node_pair); 00806 } 00807 00808 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00809 typename TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgesBegin() 00810 { 00811 unsigned first_element_index=0; 00812 while (first_element_index!=this->GetNumAllElements() && this->GetElement(first_element_index)->IsDeleted()) 00813 { 00814 first_element_index++; 00815 } 00816 return EdgeIterator(*this, first_element_index); 00817 } 00818 00819 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00820 typename TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgeIterator TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::EdgesEnd() 00821 { 00822 return EdgeIterator(*this, this->GetNumAllElements()); 00823 } 00824 00825 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00826 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::RefreshMesh() 00827 { 00828 RefreshJacobianCachedData(); 00829 } 00830 00831 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00832 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::SolveNodeMapping(unsigned index) const 00833 { 00834 assert(index < this->mNodes.size() ); 00835 return index; 00836 } 00837 00838 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00839 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::SolveElementMapping(unsigned index) const 00840 { 00841 assert(index < this->mElements.size() ); 00842 return index; 00843 } 00844 00845 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00846 unsigned TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::SolveBoundaryElementMapping(unsigned index) const 00847 { 00848 assert(index < this->mBoundaryElements.size() ); 00849 return index; 00850 } 00851 00852 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00853 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::RefreshJacobianCachedData() 00854 { 00855 unsigned num_elements = this->GetNumAllElements(); 00856 unsigned num_boundary_elements = this->GetNumAllBoundaryElements(); 00857 00858 // Make sure we have enough space 00859 this->mElementJacobians.resize(num_elements); 00860 this->mElementInverseJacobians.resize(num_elements); 00861 00862 if (ELEMENT_DIM < SPACE_DIM) 00863 { 00864 this->mElementWeightedDirections.resize(num_elements); 00865 } 00866 00867 this->mBoundaryElementWeightedDirections.resize(num_boundary_elements); 00868 00869 this->mElementJacobianDeterminants.resize(num_elements); 00870 this->mBoundaryElementJacobianDeterminants.resize(num_boundary_elements); 00871 00872 // Update caches 00873 for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin(); 00874 iter != this->GetElementIteratorEnd(); 00875 ++iter) 00876 { 00877 unsigned index = iter->GetIndex(); 00878 iter->CalculateInverseJacobian(this->mElementJacobians[index], this->mElementJacobianDeterminants[index], this->mElementInverseJacobians[index]); 00879 } 00880 00881 if (ELEMENT_DIM < SPACE_DIM) 00882 { 00883 for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin(); 00884 iter != this->GetElementIteratorEnd(); 00885 ++iter) 00886 { 00887 unsigned index = iter->GetIndex(); 00888 iter->CalculateWeightedDirection(this->mElementWeightedDirections[index], this->mElementJacobianDeterminants[index]); 00889 } 00890 } 00891 00892 for ( typename TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::BoundaryElementIterator itb = this->GetBoundaryElementIteratorBegin(); 00893 itb != this->GetBoundaryElementIteratorEnd(); 00894 itb++) 00895 { 00896 unsigned index = (*itb)->GetIndex(); 00897 (*itb)->CalculateWeightedDirection(this->mBoundaryElementWeightedDirections[index], this->mBoundaryElementJacobianDeterminants[index]); 00898 } 00899 } 00900 00901 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00902 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetJacobianForElement(unsigned elementIndex, c_matrix<double, SPACE_DIM, SPACE_DIM>& rJacobian, double& rJacobianDeterminant) const 00903 { 00904 assert(ELEMENT_DIM <= SPACE_DIM); 00905 assert(elementIndex < this->mElementJacobians.size()); 00906 rJacobian = this->mElementJacobians[elementIndex]; 00907 rJacobianDeterminant = this->mElementJacobianDeterminants[elementIndex]; 00908 } 00909 00910 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00911 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetInverseJacobianForElement(unsigned elementIndex, c_matrix<double, SPACE_DIM, ELEMENT_DIM>& rJacobian, double& rJacobianDeterminant, c_matrix<double, ELEMENT_DIM, SPACE_DIM>& rInverseJacobian) const 00912 { 00913 assert(ELEMENT_DIM <= SPACE_DIM); 00914 assert(elementIndex < this->mElementInverseJacobians.size()); 00915 rInverseJacobian = this->mElementInverseJacobians[elementIndex]; 00916 rJacobian = this->mElementJacobians[elementIndex]; 00917 rJacobianDeterminant = this->mElementJacobianDeterminants[elementIndex]; 00918 } 00919 00920 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00921 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetWeightedDirectionForElement(unsigned elementIndex, c_vector<double, SPACE_DIM>& rWeightedDirection, double& rJacobianDeterminant) const 00922 { 00923 assert(ELEMENT_DIM < SPACE_DIM); 00924 assert(elementIndex < this->mElementWeightedDirections.size()); 00925 rWeightedDirection = this->mElementWeightedDirections[elementIndex]; 00926 rJacobianDeterminant = this->mElementJacobianDeterminants[elementIndex]; 00927 } 00928 00929 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00930 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetWeightedDirectionForBoundaryElement(unsigned elementIndex, c_vector<double, SPACE_DIM>& rWeightedDirection, double& rJacobianDeterminant) const 00931 { 00932 assert(elementIndex < this->mBoundaryElementWeightedDirections.size()); 00933 rWeightedDirection = this->mBoundaryElementWeightedDirections[elementIndex]; 00934 rJacobianDeterminant = this->mBoundaryElementJacobianDeterminants[elementIndex]; 00935 } 00936 00937 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00938 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::InitialiseTriangulateIo(triangulateio& mesherIo) 00939 { 00940 mesherIo.numberofpoints = 0; 00941 mesherIo.pointlist = NULL; 00942 mesherIo.numberofpointattributes = 0; 00943 mesherIo.pointattributelist = (double *) NULL; 00944 mesherIo.pointmarkerlist = (int *) NULL; 00945 mesherIo.numberofsegments = 0; 00946 mesherIo.numberofholes = 0; 00947 mesherIo.numberofregions = 0; 00948 mesherIo.trianglelist = (int *) NULL; 00949 mesherIo.triangleattributelist = (double *) NULL; 00950 mesherIo.numberoftriangleattributes = 0; 00951 mesherIo.edgelist = (int *) NULL; 00952 mesherIo.edgemarkerlist = (int *) NULL; 00953 } 00954 00955 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00956 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::FreeTriangulateIo(triangulateio& mesherIo) 00957 { 00958 if (mesherIo.numberofpoints != 0) 00959 { 00960 mesherIo.numberofpoints=0; 00961 free(mesherIo.pointlist); 00962 } 00963 00964 // These (and the above) should actually be safe since we explicity set to NULL above 00965 free(mesherIo.pointattributelist); 00966 free(mesherIo.pointmarkerlist); 00967 free(mesherIo.trianglelist); 00968 free(mesherIo.triangleattributelist); 00969 free(mesherIo.edgelist); 00970 free(mesherIo.edgemarkerlist); 00971 } 00972 00973 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00974 template <class MESHER_IO> 00975 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ExportToMesher(NodeMap& map, MESHER_IO& mesherInput, int *elementList) 00976 { 00977 if (SPACE_DIM == 2) 00978 { 00979 mesherInput.pointlist = (double *) malloc(this->GetNumNodes() * SPACE_DIM * sizeof(double)); 00980 } 00981 else 00982 { 00983 mesherInput.pointlist = new double[this->GetNumNodes() * SPACE_DIM]; 00984 } 00985 00986 mesherInput.numberofpoints = this->GetNumNodes(); 00987 unsigned new_index = 0; 00988 for (unsigned i=0; i<this->GetNumAllNodes(); i++) 00989 { 00990 if (this->mNodes[i]->IsDeleted()) 00991 { 00992 map.SetDeleted(i); 00993 } 00994 else 00995 { 00996 map.SetNewIndex(i, new_index); 00997 for (unsigned j=0; j<SPACE_DIM; j++) 00998 { 00999 mesherInput.pointlist[SPACE_DIM*new_index + j] = this->mNodes[i]->rGetLocation()[j]; 01000 } 01001 new_index++; 01002 } 01003 } 01004 if (elementList != NULL) 01005 { 01006 unsigned element_index = 0; 01007 01008 // Assume there is enough space for this 01009 mesherInput.numberofcorners=ELEMENT_DIM+1; 01010 for (typename TetrahedralMesh<ELEMENT_DIM,SPACE_DIM>::ElementIterator elem_iter = this->GetElementIteratorBegin(); 01011 elem_iter != this->GetElementIteratorEnd(); 01012 ++elem_iter) 01013 { 01014 01015 for (unsigned j=0; j<=ELEMENT_DIM; j++) 01016 { 01017 elementList[element_index*(ELEMENT_DIM+1) + j] = (*elem_iter).GetNodeGlobalIndex(j); 01018 } 01019 element_index++; 01020 } 01021 } 01022 } 01023 01024 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 01025 template <class MESHER_IO> 01026 void TetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ImportFromMesher(MESHER_IO& mesherOutput, unsigned numberOfElements, int *elementList, unsigned numberOfFaces, int *faceList, int *edgeMarkerList) 01027 { 01028 unsigned nodes_per_element = mesherOutput.numberofcorners; 01029 01030 assert( nodes_per_element == ELEMENT_DIM+1 || nodes_per_element == (ELEMENT_DIM+1)*(ELEMENT_DIM+2)/2 ); 01031 01032 Clear(); 01033 01034 // Construct the nodes 01035 for (unsigned node_index=0; node_index<(unsigned)mesherOutput.numberofpoints; node_index++) 01036 { 01037 this->mNodes.push_back(new Node<SPACE_DIM>(node_index, &mesherOutput.pointlist[node_index * SPACE_DIM], false)); 01038 } 01039 01040 // Construct the elements 01041 this->mElements.reserve(numberOfElements); 01042 01043 unsigned real_element_index=0; 01044 for (unsigned element_index=0; element_index<numberOfElements; element_index++) 01045 { 01046 std::vector<Node<SPACE_DIM>*> nodes; 01047 for (unsigned j=0; j<ELEMENT_DIM+1; j++) 01048 { 01049 unsigned global_node_index = elementList[element_index*(nodes_per_element) + j]; 01050 assert(global_node_index < this->mNodes.size()); 01051 nodes.push_back(this->mNodes[global_node_index]); 01052 01053 } 01054 01055 /* 01056 * For some reason, tetgen in library mode makes its initial Delaunay mesh 01057 * with very thin slivers. Hence we expect to ignore some of the elements! 01058 */ 01059 Element<ELEMENT_DIM, SPACE_DIM>* p_element; 01060 try 01061 { 01062 p_element = new Element<ELEMENT_DIM, SPACE_DIM>(real_element_index, nodes); 01063 01064 // Shouldn't throw after this point 01065 this->mElements.push_back(p_element); 01066 01067 // Add the internals to quadratics 01068 for (unsigned j=ELEMENT_DIM+1; j<nodes_per_element; j++) 01069 { 01070 unsigned global_node_index = elementList[element_index*nodes_per_element + j]; 01071 assert(global_node_index < this->mNodes.size()); 01072 this->mElements[real_element_index]->AddNode( this->mNodes[global_node_index] ); 01073 this->mNodes[global_node_index]->AddElement(real_element_index); 01074 this->mNodes[global_node_index]->MarkAsInternal(); 01075 } 01076 real_element_index++; 01077 } 01078 catch (Exception &e) 01079 { 01080 if (SPACE_DIM == 2) 01081 { 01082 WARNING("Triangle has produced a zero area (collinear) element"); 01083 } 01084 else 01085 { 01086 WARNING("Tetgen has produced a zero volume (coplanar) element"); 01087 } 01088 } 01089 } 01090 01091 // Construct the BoundaryElements (and mark boundary nodes) 01092 unsigned next_boundary_element_index = 0; 01093 for (unsigned boundary_element_index=0; boundary_element_index<numberOfFaces; boundary_element_index++) 01094 { 01095 /* 01096 * Tetgen produces only boundary faces (set edgeMarkerList to NULL). 01097 * Triangle marks which edges are on the boundary. 01098 */ 01099 if (edgeMarkerList == NULL || edgeMarkerList[boundary_element_index] == 1) 01100 { 01101 std::vector<Node<SPACE_DIM>*> nodes; 01102 for (unsigned j=0; j<ELEMENT_DIM; j++) 01103 { 01104 unsigned global_node_index = faceList[boundary_element_index*ELEMENT_DIM + j]; 01105 assert(global_node_index < this->mNodes.size()); 01106 nodes.push_back(this->mNodes[global_node_index]); 01107 if (!nodes[j]->IsBoundaryNode()) 01108 { 01109 nodes[j]->SetAsBoundaryNode(); 01110 this->mBoundaryNodes.push_back(nodes[j]); 01111 } 01112 } 01113 01114 /* 01115 * For some reason, tetgen in library mode makes its initial Delaunay mesh 01116 * with very thin slivers. Hence we expect to ignore some of the elements! 01117 */ 01118 BoundaryElement<ELEMENT_DIM-1, SPACE_DIM>* p_b_element; 01119 try 01120 { 01121 p_b_element = new BoundaryElement<ELEMENT_DIM-1, SPACE_DIM>(next_boundary_element_index, nodes); 01122 this->mBoundaryElements.push_back(p_b_element); 01123 next_boundary_element_index++; 01124 } 01125 catch (Exception &e) 01126 { 01127 // Tetgen is feeding us lies //Watch this space for coverage 01128 assert(SPACE_DIM == 3); 01129 } 01130 } 01131 } 01132 01133 this->RefreshJacobianCachedData(); 01134 } 01135 01137 // Explicit instantiation 01139 01140 template class TetrahedralMesh<1,1>; 01141 template class TetrahedralMesh<1,2>; 01142 template class TetrahedralMesh<1,3>; 01143 template class TetrahedralMesh<2,2>; 01144 template class TetrahedralMesh<2,3>; 01145 template class TetrahedralMesh<3,3>; 01146 01151 template void TetrahedralMesh<2,2>::ExportToMesher<triangulateio>(NodeMap&, triangulateio&, int*); 01152 template void TetrahedralMesh<2,2>::ImportFromMesher<triangulateio>(triangulateio&, unsigned, int *, unsigned, int *, int *); 01153 01154 template void TetrahedralMesh<3,3>::ExportToMesher<tetgen::tetgenio>(NodeMap&, tetgen::tetgenio&, int*); 01155 template void TetrahedralMesh<3,3>::ImportFromMesher<tetgen::tetgenio>(tetgen::tetgenio&, unsigned, int *, unsigned, int *, int *); 01156 01157 //The following don't ever need to be instantiated, but are needed to keep some compilers happy 01158 template void TetrahedralMesh<1,2>::ExportToMesher<triangulateio>(NodeMap&, triangulateio&, int*); 01159 template void TetrahedralMesh<1,2>::ImportFromMesher<triangulateio>(triangulateio&, unsigned, int *, unsigned, int *, int *); 01160 01161 template void TetrahedralMesh<1,3>::ExportToMesher<tetgen::tetgenio>(NodeMap&, tetgen::tetgenio&, int*); 01162 template void TetrahedralMesh<1,3>::ImportFromMesher<tetgen::tetgenio>(tetgen::tetgenio&, unsigned, int *, unsigned, int *, int *); 01163 template void TetrahedralMesh<2,3>::ExportToMesher<tetgen::tetgenio>(NodeMap&, tetgen::tetgenio&, int*); 01164 template void TetrahedralMesh<2,3>::ImportFromMesher<tetgen::tetgenio>(tetgen::tetgenio&, unsigned, int *, unsigned, int *, int *); 01165 01166 //Intel compilation with IPO thinks that it's missing some bizarre instantiations 01167 template void TetrahedralMesh<3u, 3u>::ImportFromMesher<triangulateio>(triangulateio&, unsigned int, int*, unsigned int, int*, int*); 01168 template void TetrahedralMesh<1u, 1u>::ImportFromMesher<triangulateio>(triangulateio&, unsigned int, int*, unsigned int, int*, int*); 01169 template void TetrahedralMesh<1u, 1u>::ImportFromMesher<tetgen::tetgenio>(tetgen::tetgenio&, unsigned int, int*, unsigned int, int*, int*); 01170 template void TetrahedralMesh<2u, 2u>::ImportFromMesher<tetgen::tetgenio>(tetgen::tetgenio&, unsigned int, int*, unsigned int, int*, int*); 01171 template void TetrahedralMesh<1u, 1u>::ExportToMesher<tetgen::tetgenio>(NodeMap&, tetgen::tetgenio&, int*); 01172 01173 // Intel v11 compilation thinks that it's missing even more bizarre instantiations 01174 //template void TetrahedralMesh<2,2>::ExportToMesher<tetgen::tetgenio>(NodeMap&, tetgen::tetgenio&, int*); 01175 //template void TetrahedralMesh<3,3>::ExportToMesher<triangulateio>(NodeMap&, triangulateio&, int*); 01176 //template void TetrahedralMesh<1,3>::ExportToMesher<triangulateio>(NodeMap&, triangulateio&, int*); 01177 //template void TetrahedralMesh<1,1>::ExportToMesher<triangulateio>(NodeMap&, triangulateio&, int*); 01178 //template void TetrahedralMesh<1,2>::ExportToMesher<tetgen::tetgenio>(NodeMap&, tetgen::tetgenio&, int*); 01179 //template void TetrahedralMesh<2,3>::ExportToMesher<triangulateio>(NodeMap&, triangulateio&, int*); 01180 //template void TetrahedralMesh<1,3>::ImportFromMesher<triangulateio>(triangulateio&, unsigned, int *, unsigned, int *, int *); 01181 //template void TetrahedralMesh<2,3>::ImportFromMesher<triangulateio>(triangulateio&, unsigned, int *, unsigned, int *, int *); 01182 //template void TetrahedralMesh<1,2>::ImportFromMesher<tetgen::tetgenio>(tetgen::tetgenio&, unsigned, int *, unsigned, int *, int *); 01188 // Serialization for Boost >= 1.36 01189 #define CHASTE_SERIALIZATION_CPP 01190 #include "SerializationExportWrapper.hpp" 01191 EXPORT_TEMPLATE_CLASS_ALL_DIMS(TetrahedralMesh)