HeartGeometryInformation.cpp

00001 /*
00002 
00003 Copyright (c) 2005-2015, 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,
00159         std::set<unsigned>& rSurfaceNodeIndexSet,
00160         unsigned offset) const
00161 {
00162     std::stringstream line_stream(line);
00163     while (!line_stream.eof())
00164     {
00165         unsigned item;
00166         line_stream >> item;
00167         // If offset==1 then shift the nodes, since we are assuming MEMFEM format (numbered from 1 on)
00168         if (item == 0 && offset != 0)
00169         {
00170             EXCEPTION("Error when reading surface file.  It was assumed not to be indexed from zero, but zero appeared in the list.");
00171         }
00172         rSurfaceNodeIndexSet.insert(item-offset);
00173     }
00174 }
00175 
00176 
00177 template<unsigned SPACE_DIM>
00178 void HeartGeometryInformation<SPACE_DIM>::GetNodesAtSurface(
00179         const std::string& surfaceFile,
00180         std::vector<unsigned>& rSurfaceNodes,
00181         bool indexFromZero) const
00182 {
00183     // Open the file defining the surface
00184     std::ifstream file_stream;
00185     unsigned offset=0;
00186     if (indexFromZero == false)
00187     {
00188         offset=1;
00189     }
00190 
00191     file_stream.open(surfaceFile.c_str());
00192     if (!file_stream.is_open())
00193     {
00194         EXCEPTION("Wrong surface definition file name " + surfaceFile);
00195     }
00196 
00197     // Temporary storage for the nodes, helps discarding repeated values
00198     std::set<unsigned> surface_original_node_index_set;
00199 
00200     // Loop over all the triangles and add node indexes to the set
00201     std::string line;
00202     getline(file_stream, line);
00203     do
00204     {
00205         ProcessLine(line, surface_original_node_index_set, offset);
00206 
00207         getline(file_stream, line);
00208     }
00209     while(!file_stream.eof());
00210     file_stream.close();
00211 
00212     // Make vector big enough
00213     rSurfaceNodes.reserve(surface_original_node_index_set.size());
00214 
00215     if (mpMesh->rGetNodePermutation().empty())
00216     {
00217         // Copy the node indexes from the set to the vector as they are
00218         for(std::set<unsigned>::iterator node_index_it=surface_original_node_index_set.begin();
00219             node_index_it != surface_original_node_index_set.end();
00220             node_index_it++)
00221         {
00222             rSurfaceNodes.push_back(*node_index_it);
00223         }
00224     }
00225     else
00226     {
00227         // Copy the original node indices from the set to the vector applying the permutation
00228         for(std::set<unsigned>::iterator node_index_it=surface_original_node_index_set.begin();
00229             node_index_it != surface_original_node_index_set.end();
00230             node_index_it++)
00231         {
00232             rSurfaceNodes.push_back(mpMesh->rGetNodePermutation()[*node_index_it]);
00233         }
00234     }
00235 }
00236 
00237 
00238 
00239 template<unsigned SPACE_DIM>
00240 HeartRegionType HeartGeometryInformation<SPACE_DIM>::GetHeartRegion(unsigned nodeIndex) const
00241 {
00242 
00243     if (mDistMapRightVentricle[nodeIndex] >= mDistMapEpicardium[nodeIndex] &&
00244         mDistMapRightVentricle[nodeIndex] >= mDistMapLeftVentricle[nodeIndex])
00245     {
00246         return LEFT_VENTRICLE_WALL;
00247     }
00248 
00249     if (mDistMapLeftVentricle[nodeIndex] >= mDistMapEpicardium[nodeIndex] &&
00250         mDistMapLeftVentricle[nodeIndex] >= mDistMapRightVentricle[nodeIndex])
00251     {
00252         return RIGHT_VENTRICLE_WALL;
00253     }
00254 
00255     if (mDistMapEpicardium[nodeIndex] >= mDistMapLeftVentricle[nodeIndex] &&
00256         mDistMapEpicardium[nodeIndex] >= mDistMapRightVentricle[nodeIndex])
00257     {
00258         if (mDistMapLeftVentricle[nodeIndex]
00259             < LEFT_SEPTUM_SIZE*(mDistMapLeftVentricle[nodeIndex] + mDistMapRightVentricle[nodeIndex]))
00260         {
00261             return LEFT_SEPTUM;
00262         }
00263         else
00264         {
00265             return RIGHT_SEPTUM;
00266         }
00267     }
00268 
00269     return UNKNOWN;
00270 }
00271 
00272 template<unsigned SPACE_DIM>
00273 double HeartGeometryInformation<SPACE_DIM>::GetDistanceToEndo(unsigned nodeIndex)
00274 {
00275     // General case where you provide 3 surfaces: LV, RV, epicardium
00276     if ( mNumberOfSurfacesProvided == 3)
00277     {
00278         HeartRegionType node_region = GetHeartRegion(nodeIndex);
00279         switch(node_region)
00280         {
00281             case LEFT_VENTRICLE_WALL:
00282             case LEFT_VENTRICLE_SURFACE:
00283                 return mDistMapLeftVentricle[nodeIndex];
00284                 break;
00285 
00286             case RIGHT_VENTRICLE_WALL:
00287             case RIGHT_VENTRICLE_SURFACE:
00288                 return mDistMapRightVentricle[nodeIndex];
00289                 break;
00290 
00291             case LEFT_SEPTUM:
00292                 return mDistMapLeftVentricle[nodeIndex];
00293                 break;
00294 
00295             case RIGHT_SEPTUM:
00296                 return mDistMapRightVentricle[nodeIndex] ;
00297                 break;
00298 
00299             case UNKNOWN:
00300                 #define COVERAGE_IGNORE
00301                 std::cerr << "Wrong distances node: " << nodeIndex << "\t"
00302                           << "Epi " << mDistMapEpicardium[nodeIndex] << "\t"
00303                           << "RV " << mDistMapRightVentricle[nodeIndex] << "\t"
00304                           << "LV " << mDistMapLeftVentricle[nodeIndex]
00305                           << std::endl;
00306 
00307                 // Make wall_thickness=0 as in Martin's code
00308                 return 0.0;
00309                 break;
00310                 #undef COVERAGE_IGNORE
00311 
00312             default:
00313                 NEVER_REACHED;
00314         }
00315     }
00316     // Simplified case where you only provide epi and endo surface definitions
00317     else
00318     {
00319         return mDistMapEndocardium[nodeIndex];
00320     }
00321 
00322     // gcc wants to see a return statement at the end of the method.
00323     NEVER_REACHED;
00324     return 0.0;
00325 }
00326 
00327 template<unsigned SPACE_DIM>
00328 double HeartGeometryInformation<SPACE_DIM>::GetDistanceToEpi(unsigned nodeIndex)
00329 {
00330     return mDistMapEpicardium[nodeIndex];
00331 }
00332 
00333 template<unsigned SPACE_DIM>
00334 double HeartGeometryInformation<SPACE_DIM>::CalculateRelativeWallPosition(unsigned nodeIndex)
00335 {
00336 
00337     double dist_endo = GetDistanceToEndo(nodeIndex);
00338     double dist_epi = GetDistanceToEpi(nodeIndex);
00339     assert( (dist_endo + dist_epi) != 0 );
00340     /*
00341      *  A node contained on both epicardium and lv (or rv) surfaces has wall thickness 0/0.
00342      *  By setting its value to 0 we consider it contained only on the lv (or rv) surface.
00343      */
00344     double relative_position = dist_endo / (dist_endo + dist_epi);
00345     return relative_position;
00346 }
00347 
00348 template<unsigned SPACE_DIM>
00349 void HeartGeometryInformation<SPACE_DIM>::DetermineLayerForEachNode(double epiFraction, double endoFraction)
00350 {
00351     if (epiFraction+endoFraction>1)
00352     {
00353         EXCEPTION("The sum of fractions of epicardial and endocardial layers must be lesser than 1");
00354     }
00355 
00356     if ((endoFraction<0) || (epiFraction<0))
00357     {
00358         EXCEPTION("A fraction of a layer must be positive");
00359     }
00360 
00361     mLayerForEachNode.resize(mpMesh->GetNumNodes());
00362     for(unsigned i=0; i<mpMesh->GetNumNodes(); i++)
00363     {
00364         double position = CalculateRelativeWallPosition(i);
00365         if (position<endoFraction)
00366         {
00367             mLayerForEachNode[i] = ENDO;
00368         }
00369         else if (position<(1-epiFraction))
00370         {
00371             mLayerForEachNode[i] = MID;
00372         }
00373         else
00374         {
00375             mLayerForEachNode[i] = EPI;
00376         }
00377     }
00378 }
00379 
00380 
00381 template<unsigned SPACE_DIM>
00382 void HeartGeometryInformation<SPACE_DIM>::WriteLayerForEachNode(std::string outputDir, std::string file)
00383 {
00384     OutputFileHandler handler(outputDir,false);
00385     if (PetscTools::AmMaster())
00386     {
00387         out_stream p_file = handler.OpenOutputFile(file);
00388 
00389         assert(mLayerForEachNode.size()>0);
00390         for(unsigned i=0; i<mpMesh->GetNumNodes(); i++)
00391         {
00392             if(mLayerForEachNode[i]==EPI)
00393             {
00394                 *p_file << "0\n";
00395             }
00396             else if(mLayerForEachNode[i]==MID)
00397             {
00398                 *p_file << "1\n";
00399             }
00400             else // endo
00401             {
00402                 *p_file << "2\n";
00403             }
00404         }
00405 
00406         p_file->close();
00407     }
00408     PetscTools::Barrier("HeartGeometryInformation::WriteLayerForEachNode"); // Make other processes wait until we're done
00409 }
00410 
00411 
00412 template<unsigned SPACE_DIM>
00413 ChasteCuboid<SPACE_DIM> HeartGeometryInformation<SPACE_DIM>::CalculateBoundingBoxOfSurface(
00414         const std::vector<unsigned>& rSurfaceNodes)
00415 {
00416 
00417     assert(rSurfaceNodes.size()>0);
00418     //Set min to DBL_MAX etc.
00419     c_vector<double, SPACE_DIM> my_minimum_point = scalar_vector<double>(SPACE_DIM, DBL_MAX);
00420     //Set max to -DBL_MAX etc.
00421     c_vector<double, SPACE_DIM> my_maximum_point=-my_minimum_point;
00422 
00423     //Iterate through the set of points on the surface
00424     for (unsigned surface_index=0; surface_index<rSurfaceNodes.size(); surface_index++)
00425     {
00426         unsigned global_index=rSurfaceNodes[surface_index];
00427         if (mpMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(global_index) )
00428         {
00429             c_vector<double, SPACE_DIM> position = mpMesh->GetNode(global_index)->rGetLocation();
00430             //Update max/min
00431             for (unsigned i=0; i<SPACE_DIM; i++)
00432             {
00433                 if (position[i] < my_minimum_point[i])
00434                 {
00435                     my_minimum_point[i] = position[i];
00436                 }
00437                 if (position[i] > my_maximum_point[i])
00438                 {
00439                     my_maximum_point[i] = position[i];
00440                 }
00441             }
00442         }
00443     }
00444 
00445     //Share the local data and reduce over all processes
00446     c_vector<double, SPACE_DIM> global_minimum_point;
00447     c_vector<double, SPACE_DIM> global_maximum_point;
00448     MPI_Allreduce(&my_minimum_point[0], &global_minimum_point[0], SPACE_DIM, MPI_DOUBLE, MPI_MIN, PETSC_COMM_WORLD);
00449     MPI_Allreduce(&my_maximum_point[0], &global_maximum_point[0], SPACE_DIM, MPI_DOUBLE, MPI_MAX, PETSC_COMM_WORLD);
00450 
00451 
00452     ChastePoint<SPACE_DIM> min(global_minimum_point);
00453     ChastePoint<SPACE_DIM> max(global_maximum_point);
00454 
00455     return ChasteCuboid<SPACE_DIM>(min, max);
00456 }
00457 
00458 
00460 // Explicit instantiation
00462 
00463 //template class HeartGeometryInformation<1>;
00464 template class HeartGeometryInformation<2>;
00465 template class HeartGeometryInformation<3>;

Generated by  doxygen 1.6.2