38 #include "StreeterFibreGenerator.hpp"
43 #include "OutputFileHandler.hpp"
48 #include "Citations.hpp"
49 static PetscBool StreeterCite = PETSC_FALSE;
50 const char StreeterCitation[] =
"@article{streeter1969fiber,\n"
51 " title={Fiber orientation in the canine left ventricle during diastole and systole},\n"
52 " author={Streeter, Daniel D and Spotnitz, Henry M and Patel, Dali P and Ross, John and Sonnenblick, Edmund H},\n"
53 " journal={Circulation research},\n"
56 " pages={339--347},\n"
58 " publisher={Am Heart Assoc}\n"
61 template<
unsigned SPACE_DIM>
63 const unsigned nodeIndex,
const std::vector<double>& wallThickness)
const
65 if (nodeIndex < this->mpMesh->GetDistributedVectorFactory()->GetLow() ||
66 nodeIndex >= this->mpMesh->GetDistributedVectorFactory()->GetHigh() )
72 double average = wallThickness[nodeIndex];
73 unsigned nodes_visited = 1;
76 std::set<unsigned> visited_nodes;
77 visited_nodes.insert(nodeIndex);
90 for(
unsigned node_local_index=0;
91 node_local_index<p_containing_element->
GetNumNodes();
95 unsigned neighbour_node_index = p_neighbour_node->
GetIndex();
98 if (visited_nodes.find(neighbour_node_index) == visited_nodes.end())
100 average += wallThickness[neighbour_node_index];
101 visited_nodes.insert(neighbour_node_index);
107 return average/nodes_visited;
113 template<
unsigned SPACE_DIM>
115 const c_vector<HeartRegionType, SPACE_DIM+1>& nodesRegionsForElement)
const
119 for (
unsigned index=0; index<SPACE_DIM+1; index++)
121 switch (nodesRegionsForElement[index])
151 template<
unsigned SPACE_DIM>
154 mpGeometryInfo(NULL),
155 mApexToBase(zero_vector<
double>(SPACE_DIM)),
165 template<
unsigned SPACE_DIM>
168 delete mpGeometryInfo;
171 template<
unsigned SPACE_DIM>
173 const std::string& rEpicardiumFile,
174 const std::string& rRightVentricleFile,
175 const std::string& rLeftVentricleFile,
182 template<
unsigned SPACE_DIM>
185 *(this->mpMasterFile) << this->mpMesh->GetNumElements();
186 *(this->mpMasterFile) << std::setprecision(16);
189 template<
unsigned SPACE_DIM>
192 assert(SPACE_DIM == 3);
193 if (mpGeometryInfo == NULL)
195 EXCEPTION(
"Files defining the heart surfaces not set");
199 out_stream p_regions_file, p_thickness_file, p_ave_thickness_file;
206 p_regions_file = rOutputDirectory.
OpenOutputFile(
"node_regions.data");
207 p_thickness_file = rOutputDirectory.
OpenOutputFile(
"wall_thickness.data");
208 p_ave_thickness_file = rOutputDirectory.
OpenOutputFile(
"averaged_thickness.data");
212 if (fabs(norm_2(mApexToBase)) < DBL_EPSILON)
214 EXCEPTION(
"Apex to base vector has not been set");
218 unsigned num_nodes = this->mpMesh->GetNumNodes();
219 for (
unsigned node_index=0; node_index<num_nodes; node_index++)
221 double dist_epi, dist_endo;
223 HeartRegionType node_region = mpGeometryInfo->GetHeartRegion(node_index);
229 dist_epi = mpGeometryInfo->rGetDistanceMapEpicardium()[node_index];
230 dist_endo = mpGeometryInfo->rGetDistanceMapLeftVentricle()[node_index];
235 dist_epi = mpGeometryInfo->rGetDistanceMapEpicardium()[node_index];
236 dist_endo = mpGeometryInfo->rGetDistanceMapRightVentricle()[node_index];
240 dist_epi = mpGeometryInfo->rGetDistanceMapRightVentricle()[node_index];
241 dist_endo = mpGeometryInfo->rGetDistanceMapLeftVentricle()[node_index];
245 dist_epi = mpGeometryInfo->rGetDistanceMapLeftVentricle()[node_index];
246 dist_endo = mpGeometryInfo->rGetDistanceMapRightVentricle()[node_index];
250 #define COVERAGE_IGNORE
251 std::cerr <<
"Wrong distances node: " << node_index <<
"\t"
253 <<
"RV " << mpGeometryInfo->rGetDistanceMapRightVentricle()[node_index] <<
"\t"
254 <<
"LV " << mpGeometryInfo->rGetDistanceMapLeftVentricle()[node_index]
261 #undef COVERAGE_IGNORE
267 mWallThickness[node_index] = dist_endo / (dist_endo + dist_epi);
269 if (std::isnan(mWallThickness[node_index]))
271 #define COVERAGE_IGNORE
276 mWallThickness[node_index] = 0;
277 #undef COVERAGE_IGNORE
282 *p_regions_file << node_region*100 <<
"\n";
283 *p_thickness_file << mWallThickness[node_index] <<
"\n";
290 std::vector<double> my_averaged_wall_thickness(num_nodes);
291 for (
unsigned node_index=0; node_index<num_nodes; node_index++)
293 my_averaged_wall_thickness[node_index] = GetAveragedThicknessLocalNode(node_index, mWallThickness);
297 MPI_Allreduce(&my_averaged_wall_thickness[0], &mAveragedWallThickness[0], num_nodes,
298 MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
302 for (
unsigned node_index=0; node_index<num_nodes; node_index++)
304 *p_ave_thickness_file << mAveragedWallThickness[node_index] <<
"\n";
310 p_regions_file->close();
311 p_thickness_file->close();
312 p_ave_thickness_file->close();
316 template<
unsigned SPACE_DIM>
318 unsigned localElementIndex,
319 c_vector<double, SPACE_DIM*SPACE_DIM>& rData)
335 c_vector<double, SPACE_DIM> grad_ave_wall_thickness;
336 c_vector<double, SPACE_DIM+1> elem_nodes_ave_thickness;
337 double element_averaged_thickness = 0.0;
338 c_vector<HeartRegionType, SPACE_DIM+1> elem_nodes_region;
340 for (
unsigned local_node_index=0; local_node_index<SPACE_DIM+1; local_node_index++)
343 unsigned global_node_index = pElement->
GetNode(local_node_index)->GetIndex();
345 elem_nodes_ave_thickness[local_node_index] = mAveragedWallThickness[global_node_index];
346 elem_nodes_region[local_node_index] = mpGeometryInfo->GetHeartRegion(global_node_index);
349 element_averaged_thickness += mWallThickness[global_node_index];
352 element_averaged_thickness /= SPACE_DIM+1;
354 c_matrix<double, SPACE_DIM+1, SPACE_DIM> basis_functions( zero_matrix<double>(4u,3u) );
355 basis_functions(0,0) = basis_functions(0,1) = basis_functions(0,2) = -1.0;
356 basis_functions(1,0) = basis_functions(2,1) = basis_functions(3,2) = 1.0;
358 c_matrix<double, SPACE_DIM+1, SPACE_DIM> temp;
359 c_matrix<double, SPACE_DIM, SPACE_DIM> jacobian, inverse_jacobian;
361 unsigned element_index = pElement->
GetIndex();
362 this->mpMesh->GetInverseJacobianForElement(element_index, jacobian, jacobian_det, inverse_jacobian);
363 noalias(temp) = prod (basis_functions, inverse_jacobian);
364 noalias(grad_ave_wall_thickness) = prod(elem_nodes_ave_thickness, temp);
366 grad_ave_wall_thickness /= norm_2(grad_ave_wall_thickness);
375 c_vector<double, SPACE_DIM> fibre_direction =
VectorProduct(grad_ave_wall_thickness, mApexToBase);
376 fibre_direction /= norm_2(fibre_direction);
381 c_vector<double, SPACE_DIM> longitude_direction =
VectorProduct(grad_ave_wall_thickness, fibre_direction);
390 double alpha = GetFibreMaxAngle(elem_nodes_region) *
SmallPow( (1 - 2*element_averaged_thickness), 3 );
416 c_vector<double, SPACE_DIM> rotated_fibre_direction = fibre_direction*cos(alpha) + longitude_direction*sin(alpha);
417 c_vector<double, SPACE_DIM> rotated_longitude_direction = -fibre_direction*sin(alpha) + longitude_direction*cos(alpha);
423 assert( fabs(norm_2(rotated_fibre_direction) - 1) < 100*DBL_EPSILON );
424 assert( fabs(norm_2(grad_ave_wall_thickness) - 1) < 100*DBL_EPSILON );
425 assert( fabs(norm_2(rotated_longitude_direction) - 1) < 100*DBL_EPSILON );
427 assert( fabs(inner_prod(rotated_fibre_direction, grad_ave_wall_thickness)) < 100*DBL_EPSILON );
428 assert( fabs(inner_prod(rotated_fibre_direction, rotated_longitude_direction)) < 100*DBL_EPSILON);
429 assert( fabs(inner_prod(grad_ave_wall_thickness, rotated_longitude_direction)) < 100*DBL_EPSILON);
438 rData[0] = rotated_fibre_direction[0];
439 rData[1] = rotated_fibre_direction[1];
440 rData[2] = rotated_fibre_direction[2];
441 rData[3] = grad_ave_wall_thickness[0];
442 rData[4] = grad_ave_wall_thickness[1];
443 rData[5] = grad_ave_wall_thickness[2];
444 rData[6] = rotated_longitude_direction[0];
445 rData[7] = rotated_longitude_direction[1];
446 rData[8] = rotated_longitude_direction[2];
450 template<
unsigned SPACE_DIM>
453 double norm = norm_2(apexToBase);
454 if (norm < DBL_EPSILON)
456 EXCEPTION(
"Apex to base vector should be non-zero");
458 mApexToBase = apexToBase / norm;
461 template<
unsigned SPACE_DIM>
464 if (axis >= SPACE_DIM)
466 EXCEPTION(
"Apex to base coordinate axis was out of range");
468 mApexToBase = zero_vector<double>(SPACE_DIM);
469 mApexToBase[axis] = 1.0;
472 template<
unsigned SPACE_DIM>
481 #define COVERAGE_IGNORE
483 #undef COVERAGE_IGNORE
void SetLogInfo(bool logInfo=true)
void SetApexToBase(const c_vector< double, SPACE_DIM > &apexToBase)
double SmallPow(double x, unsigned exponent)
void WriteHeaderOnMaster()
#define EXCEPTION(message)
c_vector< T, 1 > VectorProduct(const c_vector< T, 1 > &rA, const c_vector< T, 1 > &rB)
virtual unsigned GetNumNodes() const
std::vector< double > mAveragedWallThickness
Node< SPACE_DIM > * GetNode(unsigned localIndex) const
out_stream OpenOutputFile(const std::string &rFileName, std::ios_base::openmode mode=std::ios::out|std::ios::trunc) const
StreeterFibreGenerator(AbstractTetrahedralMesh< SPACE_DIM, SPACE_DIM > &rMesh)
ContainingElementIterator ContainingElementsBegin() const
double GetAveragedThicknessLocalNode(const unsigned nodeIndex, const std::vector< double > &wallThickness) const
unsigned GetNumNodes() const
void SetSurfaceFiles(const std::string &rEpicardiumFile, const std::string &rRightVentricleFile, const std::string &rLeftVentricleFile, bool indexFromZero)
~StreeterFibreGenerator()
double GetFibreMaxAngle(const c_vector< HeartRegionType, SPACE_DIM+1 > &nodesRegionsForElement) const
void PreWriteCalculations(OutputFileHandler &rOutputDirectory)
static void Register(const char *pCitation, PetscBool *pSet)
std::vector< double > mWallThickness
unsigned GetIndex() const
unsigned GetIndex() const
ContainingElementIterator ContainingElementsEnd() const
void Visit(Element< SPACE_DIM, SPACE_DIM > *pElement, unsigned localElementIndex, c_vector< double, SPACE_DIM *SPACE_DIM > &rData)