Chaste  Release::3.4
HeartGeometryInformation.cpp
1 /*
2 
3 Copyright (c) 2005-2016, University of Oxford.
4 All rights reserved.
5 
6 University of Oxford means the Chancellor, Masters and Scholars of the
7 University of Oxford, having an administrative office at Wellington
8 Square, Oxford OX1 2JD, UK.
9 
10 This file is part of Chaste.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions are met:
14  * Redistributions of source code must retain the above copyright notice,
15  this list of conditions and the following disclaimer.
16  * Redistributions in binary form must reproduce the above copyright notice,
17  this list of conditions and the following disclaimer in the documentation
18  and/or other materials provided with the distribution.
19  * Neither the name of the University of Oxford nor the names of its
20  contributors may be used to endorse or promote products derived from this
21  software without specific prior written permission.
22 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 
36 #include "HeartGeometryInformation.hpp"
37 
38 #include <cmath>
39 #include <fstream>
40 #include <sstream>
41 #include "OutputFileHandler.hpp"
42 #include "Exception.hpp"
43 #include "PetscTools.hpp"
44 
45 // Area of the septum considered to belong to the each ventricle (relative to 1)
46 template<unsigned SPACE_DIM>
48 
49 template<unsigned SPACE_DIM>
51 
52 template<unsigned SPACE_DIM>
54  const std::string& rEpiFile,
55  const std::string& rEndoFile,
56  bool indexFromZero)
57  : mpMesh(&rMesh)
58 {
60 
61  // Get nodes defining each surface
62  GetNodesAtSurface(rEpiFile, mEpiSurface, indexFromZero);
63  GetNodesAtSurface(rEndoFile, mEndoSurface, indexFromZero);
64 
65  // Compute the distance map of each surface
69 }
70 
71 template<unsigned SPACE_DIM>
73  const std::string& rEpiFile,
74  const std::string& rLVFile,
75  const std::string& rRVFile,
76  bool indexFromZero)
77  : mpMesh(&rMesh)
78 {
80 
81  // Get nodes defining each surface and compute the distance map of each surface
82 
83  GetNodesAtSurface(rEpiFile, mEpiSurface, indexFromZero);
84 
85  if (rLVFile != "")
86  {
87  GetNodesAtSurface(rLVFile, mLVSurface, indexFromZero);
88  }
89  else
90  {
91  if (rRVFile == "")
92  {
93  EXCEPTION("At least one of left ventricle or right ventricle files is required");
94  }
95  }
96  if (rRVFile != "")
97  {
98  GetNodesAtSurface(rRVFile, mRVSurface, indexFromZero);
99  }
100  distance_calculator.ComputeDistanceMap(mEpiSurface, mDistMapEpicardium);
103 
105 }
106 
107 
108 template<unsigned SPACE_DIM>
110 {
111  mpMesh = NULL;
112  std::ifstream heterogeneity_file;
113  heterogeneity_file.open(nodeHeterogeneityFileName.c_str());
114 
115  if (!(heterogeneity_file.is_open()))
116  {
117  heterogeneity_file.close();
118  EXCEPTION("Could not open heterogeneities file (" << nodeHeterogeneityFileName << ")");
119  }
120 
121  while(!heterogeneity_file.eof())
122  {
123  int value;
124 
125  heterogeneity_file >> value;
126 
127  // format error (for example read a double), or value not equal to 0, 1, or 2.
128  if( (heterogeneity_file.fail() && !heterogeneity_file.eof()) || value < 0 || value > 2)
129  {
130  heterogeneity_file.close();
131  EXCEPTION("A value in the heterogeneities file (" << nodeHeterogeneityFileName
132  << ") is out of range (or not an integer). It should be epi = 0, mid = 1, endo = 2");
133  }
134 
135  if(!heterogeneity_file.eof())
136  {
137  if(value==0)
138  {
139  mLayerForEachNode.push_back(EPI);
140  }
141  else if(value==1)
142  {
143  mLayerForEachNode.push_back(MID);
144  }
145  else
146  {
147  assert(value==2);
148  mLayerForEachNode.push_back(ENDO);
149  }
150  }
151  }
152 
153  heterogeneity_file.close();
154 }
155 
156 template<unsigned SPACE_DIM>
158  const std::string& rLineFromFile,
159  std::set<unsigned>& rSurfaceNodeIndexSet,
160  unsigned offset) const
161 {
162  std::stringstream line_stream(rLineFromFile);
163  while (!line_stream.eof())
164  {
165  unsigned item;
166  line_stream >> item;
167  // If offset==1 then shift the nodes, since we are assuming MEMFEM format (numbered from 1 on)
168  if (item == 0 && offset != 0)
169  {
170  EXCEPTION("Error when reading surface file. It was assumed not to be indexed from zero, but zero appeared in the list.");
171  }
172  rSurfaceNodeIndexSet.insert(item-offset);
173  }
174 }
175 
176 
177 template<unsigned SPACE_DIM>
179  const std::string& rSurfaceFileName,
180  std::vector<unsigned>& rSurfaceNodes,
181  bool indexFromZero) const
182 {
183  // Open the file defining the surface
184  std::ifstream file_stream;
185  unsigned offset=0;
186  if (indexFromZero == false)
187  {
188  offset=1;
189  }
190 
191  file_stream.open(rSurfaceFileName.c_str());
192  if (!file_stream.is_open())
193  {
194  EXCEPTION("Wrong surface definition file name " + rSurfaceFileName);
195  }
196 
197  // Temporary storage for the nodes, helps discarding repeated values
198  std::set<unsigned> surface_original_node_index_set;
199 
200  // Loop over all the triangles and add node indexes to the set
201  std::string line;
202  getline(file_stream, line);
203  do
204  {
205  ProcessLine(line, surface_original_node_index_set, offset);
206 
207  getline(file_stream, line);
208  }
209  while(!file_stream.eof());
210  file_stream.close();
211 
212  // Make vector big enough
213  rSurfaceNodes.reserve(surface_original_node_index_set.size());
214 
215  if (mpMesh->rGetNodePermutation().empty())
216  {
217  // Copy the node indexes from the set to the vector as they are
218  for(std::set<unsigned>::iterator node_index_it=surface_original_node_index_set.begin();
219  node_index_it != surface_original_node_index_set.end();
220  node_index_it++)
221  {
222  rSurfaceNodes.push_back(*node_index_it);
223  }
224  }
225  else
226  {
227  // Copy the original node indices from the set to the vector applying the permutation
228  for(std::set<unsigned>::iterator node_index_it=surface_original_node_index_set.begin();
229  node_index_it != surface_original_node_index_set.end();
230  node_index_it++)
231  {
232  rSurfaceNodes.push_back(mpMesh->rGetNodePermutation()[*node_index_it]);
233  }
234  }
235 }
236 
237 
238 
239 template<unsigned SPACE_DIM>
241 {
242 
243  if (mDistMapRightVentricle[nodeIndex] >= mDistMapEpicardium[nodeIndex] &&
244  mDistMapRightVentricle[nodeIndex] >= mDistMapLeftVentricle[nodeIndex])
245  {
246  return LEFT_VENTRICLE_WALL;
247  }
248 
249  if (mDistMapLeftVentricle[nodeIndex] >= mDistMapEpicardium[nodeIndex] &&
250  mDistMapLeftVentricle[nodeIndex] >= mDistMapRightVentricle[nodeIndex])
251  {
252  return RIGHT_VENTRICLE_WALL;
253  }
254 
255  if (mDistMapEpicardium[nodeIndex] >= mDistMapLeftVentricle[nodeIndex] &&
256  mDistMapEpicardium[nodeIndex] >= mDistMapRightVentricle[nodeIndex])
257  {
258  if (mDistMapLeftVentricle[nodeIndex]
259  < LEFT_SEPTUM_SIZE*(mDistMapLeftVentricle[nodeIndex] + mDistMapRightVentricle[nodeIndex]))
260  {
261  return LEFT_SEPTUM;
262  }
263  else
264  {
265  return RIGHT_SEPTUM;
266  }
267  }
268 
269  return UNKNOWN;
270 }
271 
272 template<unsigned SPACE_DIM>
274 {
275  // General case where you provide 3 surfaces: LV, RV, epicardium
276  if ( mNumberOfSurfacesProvided == 3)
277  {
278  HeartRegionType node_region = GetHeartRegion(nodeIndex);
279  switch(node_region)
280  {
281  case LEFT_VENTRICLE_WALL:
282  case LEFT_VENTRICLE_SURFACE:
283  return mDistMapLeftVentricle[nodeIndex];
284  break;
285 
286  case RIGHT_VENTRICLE_WALL:
287  case RIGHT_VENTRICLE_SURFACE:
288  return mDistMapRightVentricle[nodeIndex];
289  break;
290 
291  case LEFT_SEPTUM:
292  return mDistMapLeftVentricle[nodeIndex];
293  break;
294 
295  case RIGHT_SEPTUM:
296  return mDistMapRightVentricle[nodeIndex] ;
297  break;
298 
299  case UNKNOWN:
300  #define COVERAGE_IGNORE
301  std::cerr << "Wrong distances node: " << nodeIndex << "\t"
302  << "Epi " << mDistMapEpicardium[nodeIndex] << "\t"
303  << "RV " << mDistMapRightVentricle[nodeIndex] << "\t"
304  << "LV " << mDistMapLeftVentricle[nodeIndex]
305  << std::endl;
306 
307  // Make wall_thickness=0 as in Martin's code
308  return 0.0;
309  break;
310  #undef COVERAGE_IGNORE
311 
312  default:
314  }
315  }
316  // Simplified case where you only provide epi and endo surface definitions
317  else
318  {
319  return mDistMapEndocardium[nodeIndex];
320  }
321 
322  // gcc wants to see a return statement at the end of the method.
324  return 0.0;
325 }
326 
327 template<unsigned SPACE_DIM>
329 {
330  return mDistMapEpicardium[nodeIndex];
331 }
332 
333 template<unsigned SPACE_DIM>
335 {
336 
337  double dist_endo = GetDistanceToEndo(nodeIndex);
338  double dist_epi = GetDistanceToEpi(nodeIndex);
339  assert( (dist_endo + dist_epi) != 0 );
340  /*
341  * A node contained on both epicardium and lv (or rv) surfaces has wall thickness 0/0.
342  * By setting its value to 0 we consider it contained only on the lv (or rv) surface.
343  */
344  double relative_position = dist_endo / (dist_endo + dist_epi);
345  return relative_position;
346 }
347 
348 template<unsigned SPACE_DIM>
349 void HeartGeometryInformation<SPACE_DIM>::DetermineLayerForEachNode(double epiFraction, double endoFraction)
350 {
351  if (epiFraction+endoFraction>1)
352  {
353  EXCEPTION("The sum of fractions of epicardial and endocardial layers must be lesser than 1");
354  }
355 
356  if ((endoFraction<0) || (epiFraction<0))
357  {
358  EXCEPTION("A fraction of a layer must be positive");
359  }
360 
361  mLayerForEachNode.resize(mpMesh->GetNumNodes());
362  for(unsigned i=0; i<mpMesh->GetNumNodes(); i++)
363  {
364  double position = CalculateRelativeWallPosition(i);
365  if (position<endoFraction)
366  {
367  mLayerForEachNode[i] = ENDO;
368  }
369  else if (position<(1-epiFraction))
370  {
371  mLayerForEachNode[i] = MID;
372  }
373  else
374  {
375  mLayerForEachNode[i] = EPI;
376  }
377  }
378 }
379 
380 
381 template<unsigned SPACE_DIM>
382 void HeartGeometryInformation<SPACE_DIM>::WriteLayerForEachNode(std::string outputDir, std::string file)
383 {
384  OutputFileHandler handler(outputDir,false);
385  if (PetscTools::AmMaster())
386  {
387  out_stream p_file = handler.OpenOutputFile(file);
388 
389  assert(mLayerForEachNode.size()>0);
390  for(unsigned i=0; i<mpMesh->GetNumNodes(); i++)
391  {
392  if(mLayerForEachNode[i]==EPI)
393  {
394  *p_file << "0\n";
395  }
396  else if(mLayerForEachNode[i]==MID)
397  {
398  *p_file << "1\n";
399  }
400  else // endo
401  {
402  *p_file << "2\n";
403  }
404  }
405 
406  p_file->close();
407  }
408  PetscTools::Barrier("HeartGeometryInformation::WriteLayerForEachNode"); // Make other processes wait until we're done
409 }
410 
411 
412 template<unsigned SPACE_DIM>
414  const std::vector<unsigned>& rSurfaceNodes)
415 {
416 
417  assert(rSurfaceNodes.size()>0);
418  //Set min to DBL_MAX etc.
419  c_vector<double, SPACE_DIM> my_minimum_point = scalar_vector<double>(SPACE_DIM, DBL_MAX);
420  //Set max to -DBL_MAX etc.
421  c_vector<double, SPACE_DIM> my_maximum_point=-my_minimum_point;
422 
423  //Iterate through the set of points on the surface
424  for (unsigned surface_index=0; surface_index<rSurfaceNodes.size(); surface_index++)
425  {
426  unsigned global_index=rSurfaceNodes[surface_index];
427  if (mpMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(global_index) )
428  {
429  c_vector<double, SPACE_DIM> position = mpMesh->GetNode(global_index)->rGetLocation();
430  //Update max/min
431  for (unsigned i=0; i<SPACE_DIM; i++)
432  {
433  if (position[i] < my_minimum_point[i])
434  {
435  my_minimum_point[i] = position[i];
436  }
437  if (position[i] > my_maximum_point[i])
438  {
439  my_maximum_point[i] = position[i];
440  }
441  }
442  }
443  }
444 
445  //Share the local data and reduce over all processes
446  c_vector<double, SPACE_DIM> global_minimum_point;
447  c_vector<double, SPACE_DIM> global_maximum_point;
448  MPI_Allreduce(&my_minimum_point[0], &global_minimum_point[0], SPACE_DIM, MPI_DOUBLE, MPI_MIN, PETSC_COMM_WORLD);
449  MPI_Allreduce(&my_maximum_point[0], &global_maximum_point[0], SPACE_DIM, MPI_DOUBLE, MPI_MAX, PETSC_COMM_WORLD);
450 
451 
452  ChastePoint<SPACE_DIM> min(global_minimum_point);
453  ChastePoint<SPACE_DIM> max(global_maximum_point);
454 
455  return ChasteCuboid<SPACE_DIM>(min, max);
456 }
457 
458 
460 // Explicit instantiation
462 
463 //template class HeartGeometryInformation<1>;
464 template class HeartGeometryInformation<2>;
465 template class HeartGeometryInformation<3>;
std::vector< unsigned > mEndoSurface
void DetermineLayerForEachNode(double epiFraction, double endoFraction)
std::vector< double > mDistMapEpicardium
std::vector< double > mDistMapRightVentricle
static void Barrier(const std::string callerId="")
Definition: PetscTools.cpp:134
#define EXCEPTION(message)
Definition: Exception.hpp:143
void WriteLayerForEachNode(std::string outputDir, std::string file)
static bool AmMaster()
Definition: PetscTools.cpp:120
void GetNodesAtSurface(const std::string &rSurfaceFileName, std::vector< unsigned > &rSurfaceNodes, bool indexFromZero=true) const
std::vector< unsigned > mRVSurface
#define NEVER_REACHED
Definition: Exception.hpp:206
double CalculateRelativeWallPosition(unsigned nodeIndex)
out_stream OpenOutputFile(const std::string &rFileName, std::ios_base::openmode mode=std::ios::out|std::ios::trunc) const
ChasteCuboid< SPACE_DIM > CalculateBoundingBoxOfSurface(const std::vector< unsigned > &rSurfaceNodes)
double GetDistanceToEpi(unsigned nodeIndex)
HeartGeometryInformation(AbstractTetrahedralMesh< SPACE_DIM, SPACE_DIM > &rMesh, const std::string &rEpiFile, const std::string &rEndoFile, bool indexFromZero)
void ProcessLine(const std::string &rLineFromFile, std::set< unsigned > &rSurfaceNodeIndexSet, unsigned offset) const
std::vector< double > mDistMapLeftVentricle
HeartRegionType GetHeartRegion(unsigned nodeIndex) const
void ComputeDistanceMap(const std::vector< unsigned > &rSourceNodeIndices, std::vector< double > &rNodeDistances)
double GetDistanceToEndo(unsigned nodeIndex)
AbstractTetrahedralMesh< SPACE_DIM, SPACE_DIM > * mpMesh
std::vector< double > mDistMapEndocardium
std::vector< unsigned > mLVSurface
std::vector< unsigned > mEpiSurface