Chaste Release::3.1
HeartGeometryInformation.cpp
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>;