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 "HeartGeometryInformation.hpp" 00037 00038 #include <cmath> 00039 #include <fstream> 00040 #include <sstream> 00041 #include "OutputFileHandler.hpp" 00042 #include "Exception.hpp" 00043 #include "PetscTools.hpp" 00044 00045 // Area of the septum considered to belong to the each ventricle (relative to 1) 00046 template<unsigned SPACE_DIM> 00047 const double HeartGeometryInformation<SPACE_DIM>::LEFT_SEPTUM_SIZE = 2.0/3.0; 00048 00049 template<unsigned SPACE_DIM> 00050 const double HeartGeometryInformation<SPACE_DIM>::RIGHT_SEPTUM_SIZE = 1.0/3.0; 00051 00052 template<unsigned SPACE_DIM> 00053 HeartGeometryInformation<SPACE_DIM>::HeartGeometryInformation(AbstractTetrahedralMesh<SPACE_DIM,SPACE_DIM>& rMesh, 00054 const std::string& rEpiFile, 00055 const std::string& rEndoFile, 00056 bool indexFromZero) 00057 : mpMesh(&rMesh) 00058 { 00059 DistanceMapCalculator<SPACE_DIM, SPACE_DIM> distance_calculator(*mpMesh); 00060 00061 // Get nodes defining each surface 00062 GetNodesAtSurface(rEpiFile, mEpiSurface, indexFromZero); 00063 GetNodesAtSurface(rEndoFile, mEndoSurface, indexFromZero); 00064 00065 // Compute the distance map of each surface 00066 distance_calculator.ComputeDistanceMap(mEpiSurface, mDistMapEpicardium); 00067 distance_calculator.ComputeDistanceMap(mEndoSurface, mDistMapEndocardium); 00068 mNumberOfSurfacesProvided = 2; 00069 } 00070 00071 template<unsigned SPACE_DIM> 00072 HeartGeometryInformation<SPACE_DIM>::HeartGeometryInformation (AbstractTetrahedralMesh<SPACE_DIM,SPACE_DIM>& rMesh, 00073 const std::string& rEpiFile, 00074 const std::string& rLVFile, 00075 const std::string& rRVFile, 00076 bool indexFromZero) 00077 : mpMesh(&rMesh) 00078 { 00079 DistanceMapCalculator<SPACE_DIM, SPACE_DIM> distance_calculator(*mpMesh); 00080 00081 // Get nodes defining each surface and compute the distance map of each surface 00082 00083 GetNodesAtSurface(rEpiFile, mEpiSurface, indexFromZero); 00084 00085 if (rLVFile != "") 00086 { 00087 GetNodesAtSurface(rLVFile, mLVSurface, indexFromZero); 00088 } 00089 else 00090 { 00091 if (rRVFile == "") 00092 { 00093 EXCEPTION("At least one of left ventricle or right ventricle files is required"); 00094 } 00095 } 00096 if (rRVFile != "") 00097 { 00098 GetNodesAtSurface(rRVFile, mRVSurface, indexFromZero); 00099 } 00100 distance_calculator.ComputeDistanceMap(mEpiSurface, mDistMapEpicardium); 00101 distance_calculator.ComputeDistanceMap(mLVSurface, mDistMapLeftVentricle); 00102 distance_calculator.ComputeDistanceMap(mRVSurface, mDistMapRightVentricle); 00103 00104 mNumberOfSurfacesProvided = 3; 00105 } 00106 00107 00108 template<unsigned SPACE_DIM> 00109 HeartGeometryInformation<SPACE_DIM>::HeartGeometryInformation (std::string nodeHeterogeneityFileName) 00110 { 00111 mpMesh = NULL; 00112 std::ifstream heterogeneity_file; 00113 heterogeneity_file.open(nodeHeterogeneityFileName.c_str()); 00114 00115 if (!(heterogeneity_file.is_open())) 00116 { 00117 heterogeneity_file.close(); 00118 EXCEPTION("Could not open heterogeneities file (" << nodeHeterogeneityFileName << ")"); 00119 } 00120 00121 while(!heterogeneity_file.eof()) 00122 { 00123 int value; 00124 00125 heterogeneity_file >> value; 00126 00127 // format error (for example read a double), or value not equal to 0, 1, or 2. 00128 if( (heterogeneity_file.fail() && !heterogeneity_file.eof()) || value < 0 || value > 2) 00129 { 00130 heterogeneity_file.close(); 00131 EXCEPTION("A value in the heterogeneities file (" << nodeHeterogeneityFileName 00132 << ") is out of range (or not an integer). It should be epi = 0, mid = 1, endo = 2"); 00133 } 00134 00135 if(!heterogeneity_file.eof()) 00136 { 00137 if(value==0) 00138 { 00139 mLayerForEachNode.push_back(EPI); 00140 } 00141 else if(value==1) 00142 { 00143 mLayerForEachNode.push_back(MID); 00144 } 00145 else 00146 { 00147 assert(value==2); 00148 mLayerForEachNode.push_back(ENDO); 00149 } 00150 } 00151 } 00152 00153 heterogeneity_file.close(); 00154 } 00155 00156 template<unsigned SPACE_DIM> 00157 void HeartGeometryInformation<SPACE_DIM>::ProcessLine( 00158 const std::string& line, std::set<unsigned>& rSurfaceNodeIndexSet, unsigned offset) const 00159 { 00160 std::stringstream line_stream(line); 00161 while (!line_stream.eof()) 00162 { 00163 unsigned item; 00164 line_stream >> item; 00165 // If offset==1 then shift the nodes, since we are assuming MEMFEM format (numbered from 1 on) 00166 if (item == 0 && offset != 0) 00167 { 00168 EXCEPTION("Error when reading surface file. It was assumed not to be indexed from zero, but zero appeared in the list."); 00169 } 00170 rSurfaceNodeIndexSet.insert(item-offset); 00171 } 00172 } 00173 00174 00175 template<unsigned SPACE_DIM> 00176 void HeartGeometryInformation<SPACE_DIM>::GetNodesAtSurface( 00177 const std::string& surfaceFile, std::vector<unsigned>& rSurfaceNodes, bool indexFromZero) const 00178 { 00179 // Open the file defining the surface 00180 std::ifstream file_stream; 00181 unsigned offset=0; 00182 if (indexFromZero == false) 00183 { 00184 offset=1; 00185 } 00186 00187 file_stream.open(surfaceFile.c_str()); 00188 if (!file_stream.is_open()) 00189 { 00190 EXCEPTION("Wrong surface definition file name " + surfaceFile); 00191 } 00192 00193 // Temporary storage for the nodes, helps discarding repeated values 00194 std::set<unsigned> surface_original_node_index_set; 00195 00196 // Loop over all the triangles and add node indexes to the set 00197 std::string line; 00198 getline(file_stream, line); 00199 do 00200 { 00201 ProcessLine(line, surface_original_node_index_set, offset); 00202 00203 getline(file_stream, line); 00204 } 00205 while(!file_stream.eof()); 00206 file_stream.close(); 00207 00208 // Make vector big enough 00209 rSurfaceNodes.reserve(surface_original_node_index_set.size()); 00210 00211 if (mpMesh->rGetNodePermutation().empty()) 00212 { 00213 // Copy the node indexes from the set to the vector as they are 00214 for(std::set<unsigned>::iterator node_index_it=surface_original_node_index_set.begin(); 00215 node_index_it != surface_original_node_index_set.end(); 00216 node_index_it++) 00217 { 00218 rSurfaceNodes.push_back(*node_index_it); 00219 } 00220 } 00221 else 00222 { 00223 // Copy the original node indices from the set to the vector applying the permutation 00224 for(std::set<unsigned>::iterator node_index_it=surface_original_node_index_set.begin(); 00225 node_index_it != surface_original_node_index_set.end(); 00226 node_index_it++) 00227 { 00228 rSurfaceNodes.push_back(mpMesh->rGetNodePermutation()[*node_index_it]); 00229 } 00230 } 00231 } 00232 00233 00234 00235 template<unsigned SPACE_DIM> 00236 HeartRegionType HeartGeometryInformation<SPACE_DIM>::GetHeartRegion(unsigned nodeIndex) const 00237 { 00238 00239 if (mDistMapRightVentricle[nodeIndex] >= mDistMapEpicardium[nodeIndex] && 00240 mDistMapRightVentricle[nodeIndex] >= mDistMapLeftVentricle[nodeIndex]) 00241 { 00242 return LEFT_VENTRICLE_WALL; 00243 } 00244 00245 if (mDistMapLeftVentricle[nodeIndex] >= mDistMapEpicardium[nodeIndex] && 00246 mDistMapLeftVentricle[nodeIndex] >= mDistMapRightVentricle[nodeIndex]) 00247 { 00248 return RIGHT_VENTRICLE_WALL; 00249 } 00250 00251 if (mDistMapEpicardium[nodeIndex] >= mDistMapLeftVentricle[nodeIndex] && 00252 mDistMapEpicardium[nodeIndex] >= mDistMapRightVentricle[nodeIndex]) 00253 { 00254 if (mDistMapLeftVentricle[nodeIndex] 00255 < LEFT_SEPTUM_SIZE*(mDistMapLeftVentricle[nodeIndex] + mDistMapRightVentricle[nodeIndex])) 00256 { 00257 return LEFT_SEPTUM; 00258 } 00259 else 00260 { 00261 return RIGHT_SEPTUM; 00262 } 00263 } 00264 00265 return UNKNOWN; 00266 } 00267 00268 template<unsigned SPACE_DIM> 00269 double HeartGeometryInformation<SPACE_DIM>::GetDistanceToEndo(unsigned nodeIndex) 00270 { 00271 // General case where you provide 3 surfaces: LV, RV, epicardium 00272 if ( mNumberOfSurfacesProvided == 3) 00273 { 00274 HeartRegionType node_region = GetHeartRegion(nodeIndex); 00275 switch(node_region) 00276 { 00277 case LEFT_VENTRICLE_WALL: 00278 case LEFT_VENTRICLE_SURFACE: 00279 return mDistMapLeftVentricle[nodeIndex]; 00280 break; 00281 00282 case RIGHT_VENTRICLE_WALL: 00283 case RIGHT_VENTRICLE_SURFACE: 00284 return mDistMapRightVentricle[nodeIndex]; 00285 break; 00286 00287 case LEFT_SEPTUM: 00288 return mDistMapLeftVentricle[nodeIndex]; 00289 break; 00290 00291 case RIGHT_SEPTUM: 00292 return mDistMapRightVentricle[nodeIndex] ; 00293 break; 00294 00295 case UNKNOWN: 00296 #define COVERAGE_IGNORE 00297 std::cerr << "Wrong distances node: " << nodeIndex << "\t" 00298 << "Epi " << mDistMapEpicardium[nodeIndex] << "\t" 00299 << "RV " << mDistMapRightVentricle[nodeIndex] << "\t" 00300 << "LV " << mDistMapLeftVentricle[nodeIndex] 00301 << std::endl; 00302 00303 // Make wall_thickness=0 as in Martin's code 00304 return 0.0; 00305 break; 00306 #undef COVERAGE_IGNORE 00307 00308 default: 00309 NEVER_REACHED; 00310 } 00311 } 00312 // Simplified case where you only provide epi and endo surface definitions 00313 else 00314 { 00315 return mDistMapEndocardium[nodeIndex]; 00316 } 00317 00318 // gcc wants to see a return statement at the end of the method. 00319 NEVER_REACHED; 00320 return 0.0; 00321 } 00322 00323 template<unsigned SPACE_DIM> 00324 double HeartGeometryInformation<SPACE_DIM>::GetDistanceToEpi(unsigned nodeIndex) 00325 { 00326 return mDistMapEpicardium[nodeIndex]; 00327 } 00328 00329 template<unsigned SPACE_DIM> 00330 double HeartGeometryInformation<SPACE_DIM>::CalculateRelativeWallPosition(unsigned nodeIndex) 00331 { 00332 00333 double dist_endo = GetDistanceToEndo(nodeIndex); 00334 double dist_epi = GetDistanceToEpi(nodeIndex); 00335 assert( (dist_endo + dist_epi) != 0 ); 00336 /* 00337 * A node contained on both epicardium and lv (or rv) surfaces has wall thickness 0/0. 00338 * By setting its value to 0 we consider it contained only on the lv (or rv) surface. 00339 */ 00340 double relative_position = dist_endo / (dist_endo + dist_epi); 00341 return relative_position; 00342 } 00343 00344 template<unsigned SPACE_DIM> 00345 void HeartGeometryInformation<SPACE_DIM>::DetermineLayerForEachNode(double epiFraction, double endoFraction) 00346 { 00347 if (epiFraction+endoFraction>1) 00348 { 00349 EXCEPTION("The sum of fractions of epicardial and endocardial layers must be lesser than 1"); 00350 } 00351 00352 if ((endoFraction<0) || (epiFraction<0)) 00353 { 00354 EXCEPTION("A fraction of a layer must be positive"); 00355 } 00356 00357 mLayerForEachNode.resize(mpMesh->GetNumNodes()); 00358 for(unsigned i=0; i<mpMesh->GetNumNodes(); i++) 00359 { 00360 double position = CalculateRelativeWallPosition(i); 00361 if (position<endoFraction) 00362 { 00363 mLayerForEachNode[i] = ENDO; 00364 } 00365 else if (position<(1-epiFraction)) 00366 { 00367 mLayerForEachNode[i] = MID; 00368 } 00369 else 00370 { 00371 mLayerForEachNode[i] = EPI; 00372 } 00373 } 00374 } 00375 00376 00377 template<unsigned SPACE_DIM> 00378 void HeartGeometryInformation<SPACE_DIM>::WriteLayerForEachNode(std::string outputDir, std::string file) 00379 { 00380 OutputFileHandler handler(outputDir,false); 00381 if (PetscTools::AmMaster()) 00382 { 00383 out_stream p_file = handler.OpenOutputFile(file); 00384 00385 assert(mLayerForEachNode.size()>0); 00386 for(unsigned i=0; i<mpMesh->GetNumNodes(); i++) 00387 { 00388 if(mLayerForEachNode[i]==EPI) 00389 { 00390 *p_file << "0\n"; 00391 } 00392 else if(mLayerForEachNode[i]==MID) 00393 { 00394 *p_file << "1\n"; 00395 } 00396 else // endo 00397 { 00398 *p_file << "2\n"; 00399 } 00400 } 00401 00402 p_file->close(); 00403 } 00404 PetscTools::Barrier("HeartGeometryInformation::WriteLayerForEachNode"); // Make other processes wait until we're done 00405 } 00406 00407 00408 template<unsigned SPACE_DIM> 00409 ChasteCuboid<SPACE_DIM> HeartGeometryInformation<SPACE_DIM>::CalculateBoundingBoxOfSurface(const std::vector<unsigned>& rSurfaceNodes) 00410 { 00411 00412 assert(rSurfaceNodes.size()>0); 00413 //Set min to DBL_MAX etc. 00414 c_vector<double, SPACE_DIM> my_minimum_point = scalar_vector<double>(SPACE_DIM, DBL_MAX); 00415 //Set max to -DBL_MAX etc. 00416 c_vector<double, SPACE_DIM> my_maximum_point=-my_minimum_point; 00417 00418 //Iterate through the set of points on the surface 00419 for (unsigned surface_index=0; surface_index<rSurfaceNodes.size(); surface_index++) 00420 { 00421 unsigned global_index=rSurfaceNodes[surface_index]; 00422 if (mpMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(global_index) ) 00423 { 00424 c_vector<double, SPACE_DIM> position = mpMesh->GetNode(global_index)->rGetLocation(); 00425 //Update max/min 00426 for (unsigned i=0; i<SPACE_DIM; i++) 00427 { 00428 if (position[i] < my_minimum_point[i]) 00429 { 00430 my_minimum_point[i] = position[i]; 00431 } 00432 if (position[i] > my_maximum_point[i]) 00433 { 00434 my_maximum_point[i] = position[i]; 00435 } 00436 } 00437 } 00438 } 00439 00440 //Share the local data and reduce over all processes 00441 c_vector<double, SPACE_DIM> global_minimum_point; 00442 c_vector<double, SPACE_DIM> global_maximum_point; 00443 MPI_Allreduce(&my_minimum_point[0], &global_minimum_point[0], SPACE_DIM, MPI_DOUBLE, MPI_MIN, PETSC_COMM_WORLD); 00444 MPI_Allreduce(&my_maximum_point[0], &global_maximum_point[0], SPACE_DIM, MPI_DOUBLE, MPI_MAX, PETSC_COMM_WORLD); 00445 00446 00447 ChastePoint<SPACE_DIM> min(global_minimum_point); 00448 ChastePoint<SPACE_DIM> max(global_maximum_point); 00449 00450 return ChasteCuboid<SPACE_DIM>(min, max); 00451 } 00452 00453 00455 // Explicit instantiation 00457 00458 //template class HeartGeometryInformation<1>; 00459 template class HeartGeometryInformation<2>; 00460 template class HeartGeometryInformation<3>;