HeartGeometryInformation.cpp

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