00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #ifndef ABSTRACTCONVERGENCETESTER_HPP_
00038 #define ABSTRACTCONVERGENCETESTER_HPP_
00039
00040 #include "BidomainProblem.hpp"
00041 #include "MonodomainProblem.hpp"
00042
00043 #include <petscvec.h>
00044 #include <vector>
00045 #include <iostream>
00046 #include <fstream>
00047 #include <cmath>
00048 #include <ctime>
00049
00050 #include "AbstractTetrahedralMesh.hpp"
00051 #include "TrianglesMeshReader.hpp"
00052 #include "AbstractCardiacCellFactory.hpp"
00053 #include "OutputFileHandler.hpp"
00054 #include "TrianglesMeshWriter.hpp"
00055 #include "PropagationPropertiesCalculator.hpp"
00056 #include "Hdf5DataReader.hpp"
00057 #include "GeneralPlaneStimulusCellFactory.hpp"
00058 #include "CuboidMeshConstructor.hpp"
00059 #include "ZeroStimulusCellFactory.hpp"
00060 #include "SimpleStimulus.hpp"
00061 #include "ConstBoundaryCondition.hpp"
00062 #include "StimulusBoundaryCondition.hpp"
00063
00064 #include "Warnings.hpp"
00065
00066 typedef enum StimulusType_
00067 {
00068 PLANE=0,
00069 QUARTER,
00070 NEUMANN
00071 } StimulusType;
00072
00077 template <class CELL, unsigned DIM>
00078 class RampedQuarterStimulusCellFactory : public AbstractCardiacCellFactory<DIM>
00079 {
00080 private:
00082 std::vector< boost::shared_ptr<SimpleStimulus> > mpStimuli;
00084 double mMeshWidth;
00086 double mStepSize;
00090 unsigned mLevels;
00091 public:
00092
00099 RampedQuarterStimulusCellFactory(double meshWidth, unsigned numElemAcross, double fullStim=-1e7)
00100 : AbstractCardiacCellFactory<DIM>(),
00101 mMeshWidth(meshWidth),
00102 mStepSize(meshWidth/numElemAcross),
00103 mLevels(numElemAcross/4)
00104 {
00105 assert(numElemAcross%4 == 0);
00106
00107 for (unsigned level=0; level<mLevels; level++)
00108 {
00109 double this_stim = fullStim - (level*fullStim)/mLevels;
00110
00111 mpStimuli.push_back((boost::shared_ptr<SimpleStimulus>)new SimpleStimulus(this_stim, 0.5));
00112 }
00113 }
00114
00115
00121 AbstractCardiacCellInterface* CreateCardiacCellForTissueNode(Node<DIM>* pNode)
00122 {
00123 double x = pNode->GetPoint()[0];
00124 double d_level = x/mStepSize;
00125 unsigned level = (unsigned) d_level;
00126 assert(fabs(level-d_level) < DBL_MAX);
00127
00128 if (level < mLevels)
00129 {
00130 return new CELL(this->mpSolver, this->mpStimuli[level]);
00131 }
00132 else
00133 {
00134 return new CELL(this->mpSolver, this->mpZeroStimulus);
00135 }
00136 }
00137 };
00138
00139
00144 class AbstractUntemplatedConvergenceTester
00145 {
00146 protected:
00148 double mMeshWidth;
00149 public:
00151 double OdeTimeStep;
00153 double PdeTimeStep;
00158 unsigned MeshNum;
00159 double RelativeConvergenceCriterion;
00160 double LastDifference;
00161 double Apd90FirstQn;
00162 double Apd90ThirdQn;
00163 double ConductionVelocity;
00167 bool PopulatedResult;
00171 bool FixedResult;
00175 bool UseAbsoluteStimulus;
00180 double AbsoluteStimulus;
00181 bool SimulateFullActionPotential;
00182 bool Converged;
00183 StimulusType Stimulus;
00184 double NeumannStimulus;
00186 AbstractUntemplatedConvergenceTester();
00187
00193 virtual void Converge(std::string nameOfTest)=0;
00194
00195 virtual ~AbstractUntemplatedConvergenceTester();
00196 };
00197
00201 template<class CARDIAC_PROBLEM, unsigned DIM>
00202 void SetConductivities(CARDIAC_PROBLEM& rCardiacProblem);
00203
00204 template<unsigned DIM>
00205 void SetConductivities(BidomainProblem<DIM>& rProblem)
00206 {
00207 c_vector<double, DIM> conductivities;
00208 for (unsigned i=0; i<DIM; i++)
00209 {
00210 conductivities[i] = 1.75;
00211 }
00212 HeartConfig::Instance()->SetIntracellularConductivities(conductivities);
00213
00214 for (unsigned i=0; i<DIM; i++)
00215 {
00216 conductivities[i] = 7.0;
00217 }
00218 HeartConfig::Instance()->SetExtracellularConductivities(conductivities);
00219 }
00220
00221 template<unsigned DIM>
00222 void SetConductivities(MonodomainProblem<DIM>& rProblem)
00223 {
00224 c_vector<double, DIM> conductivities;
00225 for (unsigned i=0; i<DIM; i++)
00226 {
00227 conductivities[i] = 1.75;
00228 }
00229 HeartConfig::Instance()->SetIntracellularConductivities(conductivities);
00230 }
00231
00236 template<class CELL, class CARDIAC_PROBLEM, unsigned DIM, unsigned PROBLEM_DIM>
00237 class AbstractConvergenceTester : public AbstractUntemplatedConvergenceTester
00238 {
00239 public:
00244 void Converge(std::string nameOfTest)
00245 {
00246
00247 const std::string mesh_dir = "ConvergenceMesh";
00248 OutputFileHandler output_file_handler(mesh_dir);
00249 ReplicatableVector voltage_replicated;
00250
00251 unsigned file_num=0;
00252
00253
00254 OutputFileHandler conv_info_handler("ConvergencePlots"+nameOfTest, false);
00255 out_stream p_conv_info_file;
00256 if (PetscTools::AmMaster())
00257 {
00258 std::cout << "=========================== Beginning Test...==================================\n";
00259 p_conv_info_file = conv_info_handler.OpenOutputFile(nameOfTest+"_info.csv");
00260 (*p_conv_info_file) << "#Abcisa\t"
00261 << "l2-norm-full\t"
00262 << "l2-norm-onset\t"
00263 << "Max absolute err\t"
00264 << "APD90_1st_quad\t"
00265 << "APD90_3rd_quad\t"
00266 << "Conduction velocity (relative diffs)" << std::endl;
00267 }
00268 SetInitialConvergenceParameters();
00269
00270 double prev_apd90_first_qn=0.0;
00271 double prev_apd90_third_qn=0.0;
00272 double prev_cond_velocity=0.0;
00273 std::vector<double> prev_voltage;
00274 std::vector<double> prev_times;
00275 PopulateStandardResult(prev_voltage, prev_times);
00276
00277 do
00278 {
00279 CuboidMeshConstructor<DIM> constructor;
00280
00281
00282 double printing_step = this->PdeTimeStep;
00283 #define COVERAGE_IGNORE
00284 while (printing_step < 1.0e-4)
00285 {
00286 printing_step *= 2.0;
00287 std::cout<<"Warning: PrintingTimeStep increased\n";
00288 }
00289 #undef COVERAGE_IGNORE
00290 HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(this->OdeTimeStep, this->PdeTimeStep, printing_step);
00291 #define COVERAGE_IGNORE
00292 if (SimulateFullActionPotential)
00293 {
00294 HeartConfig::Instance()->SetSimulationDuration(400.0);
00295 }
00296 else
00297 {
00298 HeartConfig::Instance()->SetSimulationDuration(8.0);
00299 }
00300 #undef COVERAGE_IGNORE
00301 HeartConfig::Instance()->SetOutputDirectory("Convergence"+nameOfTest);
00302 HeartConfig::Instance()->SetOutputFilenamePrefix("Results");
00303
00304 DistributedTetrahedralMesh<DIM, DIM> mesh;
00305 constructor.Construct(mesh, this->MeshNum, mMeshWidth);
00306
00307 unsigned num_ele_across = SmallPow(2u, this->MeshNum+2);
00308
00309 AbstractCardiacCellFactory<DIM>* p_cell_factory=NULL;
00310
00311 switch (this->Stimulus)
00312 {
00313 case NEUMANN:
00314 {
00315 p_cell_factory = new ZeroStimulusCellFactory<CELL, DIM>();
00316 break;
00317 }
00318 case PLANE:
00319 {
00320 if (this->UseAbsoluteStimulus)
00321 {
00322 #define COVERAGE_IGNORE
00323 p_cell_factory = new GeneralPlaneStimulusCellFactory<CELL, DIM>(0, this->AbsoluteStimulus, true);
00324 #undef COVERAGE_IGNORE
00325 }
00326 else
00327 {
00328 p_cell_factory = new GeneralPlaneStimulusCellFactory<CELL, DIM>(num_ele_across, constructor.GetWidth(), false, this->AbsoluteStimulus);
00329 }
00330 break;
00331 }
00332 case QUARTER:
00333 {
00335 p_cell_factory = new RampedQuarterStimulusCellFactory<CELL, DIM>(constructor.GetWidth(), num_ele_across, this->AbsoluteStimulus/10.0);
00336 break;
00337 }
00338 default:
00339 NEVER_REACHED;
00340 }
00341
00342
00343 CARDIAC_PROBLEM cardiac_problem(p_cell_factory);
00344 cardiac_problem.SetMesh(&mesh);
00345
00346
00347 unsigned third_quadrant_node;
00348 unsigned first_quadrant_node;
00349 switch(DIM)
00350 {
00351 case 1:
00352 {
00353 first_quadrant_node = (unsigned) (0.25*constructor.GetNumElements());
00354 third_quadrant_node = (unsigned) (0.75*constructor.GetNumElements());
00355 break;
00356 }
00357 case 2:
00358 {
00359 unsigned n= SmallPow (2u, this->MeshNum+2);
00360 first_quadrant_node = (n+1)*(n/2)+ n/4 ;
00361 third_quadrant_node = (n+1)*(n/2)+3*n/4 ;
00362 break;
00363 }
00364 case 3:
00365 {
00366 const unsigned first_quadrant_nodes_3d[5]={61, 362, 2452, 17960, 137296};
00367 const unsigned third_quadrant_nodes_3d[5]={63, 366, 2460, 17976, 137328};
00368 assert(this->PdeTimeStep<5);
00369 first_quadrant_node = first_quadrant_nodes_3d[this->MeshNum];
00370 third_quadrant_node = third_quadrant_nodes_3d[this->MeshNum];
00371 break;
00372 }
00373
00374 default:
00375 NEVER_REACHED;
00376 }
00377
00378 double mesh_width=constructor.GetWidth();
00379
00380
00381 std::vector<unsigned> nodes_to_be_output;
00382 nodes_to_be_output.push_back(first_quadrant_node);
00383 nodes_to_be_output.push_back(third_quadrant_node);
00384 cardiac_problem.SetOutputNodes(nodes_to_be_output);
00385
00386
00387
00388
00389 SetConductivities(cardiac_problem);
00390
00391 cardiac_problem.Initialise();
00392
00393 boost::shared_ptr<BoundaryConditionsContainer<DIM,DIM,PROBLEM_DIM> > p_bcc(new BoundaryConditionsContainer<DIM,DIM,PROBLEM_DIM>);
00394 SimpleStimulus stim(NeumannStimulus, 0.5);
00395 if (Stimulus==NEUMANN)
00396 {
00397
00398 StimulusBoundaryCondition<DIM>* p_bc_stim = new StimulusBoundaryCondition<DIM>(&stim);
00399
00400
00401 AbstractTetrahedralMesh<DIM, DIM> &r_mesh = cardiac_problem.rGetMesh();
00402
00403 typename AbstractTetrahedralMesh<DIM, DIM>::BoundaryElementIterator iter;
00404 iter = r_mesh.GetBoundaryElementIteratorBegin();
00405 while (iter != r_mesh.GetBoundaryElementIteratorEnd())
00406 {
00407 double x = ((*iter)->CalculateCentroid())[0];
00409 if (x*x<=1e-10)
00410 {
00411 p_bcc->AddNeumannBoundaryCondition(*iter, p_bc_stim);
00412 }
00413 iter++;
00414 }
00415
00416 cardiac_problem.SetBoundaryConditionsContainer(p_bcc);
00417 }
00418
00419 DisplayRun();
00420 double time_before=MPI_Wtime();
00422
00423
00424 try
00425 {
00426 cardiac_problem.Solve();
00427 }
00428 catch (Exception e)
00429 {
00430 WARNING("This run threw an exception. Check convergence results\n");
00431 std::cout << e.GetMessage() << std::endl;
00432 }
00433
00434 if (PetscTools::AmMaster())
00435 {
00436 std::cout << "Time to solve = " << MPI_Wtime()-time_before << " seconds\n";
00437 }
00438
00439 OutputFileHandler results_handler("Convergence"+nameOfTest, false);
00440 Hdf5DataReader results_reader = cardiac_problem.GetDataReader();
00441
00442 {
00443 std::vector<double> transmembrane_potential = results_reader.GetVariableOverTime("V", third_quadrant_node);
00444 std::vector<double> time_series = results_reader.GetUnlimitedDimensionValues();
00445
00446 OutputFileHandler plot_file_handler("ConvergencePlots"+nameOfTest, false);
00447 if (PetscTools::AmMaster())
00448 {
00449
00450 {
00451 std::stringstream plot_file_name_stream;
00452 plot_file_name_stream<< nameOfTest << "_Third_quadrant_node_run_"<< file_num << ".csv";
00453 out_stream p_plot_file = plot_file_handler.OpenOutputFile(plot_file_name_stream.str());
00454 for (unsigned data_point = 0; data_point<time_series.size(); data_point++)
00455 {
00456 (*p_plot_file) << time_series[data_point] << "\t" << transmembrane_potential[data_point] << "\n";
00457 }
00458 p_plot_file->close();
00459 }
00460
00461 {
00462 std::vector<double> transmembrane_potential_1qd=results_reader.GetVariableOverTime("V", first_quadrant_node);
00463 std::vector<double> time_series_1qd = results_reader.GetUnlimitedDimensionValues();
00464 std::stringstream plot_file_name_stream;
00465 plot_file_name_stream<< nameOfTest << "_First_quadrant_node_run_"<< file_num << ".csv";
00466 out_stream p_plot_file = plot_file_handler.OpenOutputFile(plot_file_name_stream.str());
00467 for (unsigned data_point = 0; data_point<time_series.size(); data_point++)
00468 {
00469 (*p_plot_file) << time_series_1qd[data_point] << "\t" << transmembrane_potential_1qd[data_point] << "\n";
00470 }
00471 p_plot_file->close();
00472 }
00473 }
00474
00475 PropagationPropertiesCalculator ppc(&results_reader);
00476
00477 try
00478 {
00479 #define COVERAGE_IGNORE
00480 if (SimulateFullActionPotential)
00481 {
00482 Apd90FirstQn = ppc.CalculateActionPotentialDuration(90.0, first_quadrant_node);
00483 Apd90ThirdQn = ppc.CalculateActionPotentialDuration(90.0, third_quadrant_node);
00484 }
00485 #undef COVERAGE_IGNORE
00486 ConductionVelocity = ppc.CalculateConductionVelocity(first_quadrant_node,third_quadrant_node,0.5*mesh_width);
00487 }
00488 catch (Exception e)
00489 {
00490 #define COVERAGE_IGNORE
00491 std::cout << "Warning - this run threw an exception in calculating propagation. Check convergence results\n";
00492 std::cout << e.GetMessage() << std::endl;
00493 #undef COVERAGE_IGNORE
00494 }
00495 double cond_velocity_error = 1e10;
00496 double apd90_first_qn_error = 1e10;
00497 double apd90_third_qn_error = 1e10;
00498
00499 if (this->PopulatedResult)
00500 {
00501 if (prev_cond_velocity != 0.0)
00502 {
00503 cond_velocity_error = fabs(ConductionVelocity - prev_cond_velocity) / prev_cond_velocity;
00504 }
00505 #define COVERAGE_IGNORE
00506 if (prev_apd90_first_qn != 0.0)
00507 {
00508 apd90_first_qn_error = fabs(Apd90FirstQn - prev_apd90_first_qn) / prev_apd90_first_qn;
00509 }
00510 if (prev_apd90_third_qn != 0.0)
00511 {
00512 apd90_third_qn_error = fabs(Apd90ThirdQn - prev_apd90_third_qn) / prev_apd90_third_qn;
00513 }
00514 if (apd90_first_qn_error == 0.0)
00515 {
00516 apd90_first_qn_error = DBL_EPSILON;
00517 }
00518 if (apd90_third_qn_error == 0.0)
00519 {
00520 apd90_third_qn_error = DBL_EPSILON;
00521 }
00522 #undef COVERAGE_IGNORE
00523 if (cond_velocity_error == 0.0)
00524 {
00525 cond_velocity_error = DBL_EPSILON;
00526 }
00527 }
00528
00529 prev_cond_velocity = ConductionVelocity;
00530 prev_apd90_first_qn = Apd90FirstQn;
00531 prev_apd90_third_qn = Apd90ThirdQn;
00532
00533
00534 double max_abs_error = 0;
00535 double sum_sq_abs_error =0;
00536 double sum_sq_prev_voltage = 0;
00537 double sum_sq_abs_error_full =0;
00538 double sum_sq_prev_voltage_full = 0;
00539 if (this->PopulatedResult)
00540 {
00541
00542
00543 unsigned time_factor=(time_series.size()-1) / (prev_times.size()-1);
00544 assert (time_factor == 1 || time_factor == 2 || time_factor == 8);
00545
00546 for (unsigned data_point = 0; data_point<prev_times.size(); data_point++)
00547 {
00548 unsigned this_data_point=time_factor*data_point;
00549
00550 assert(time_series[this_data_point] == prev_times[data_point]);
00551 double abs_error = fabs(transmembrane_potential[this_data_point]-prev_voltage[data_point]);
00552 max_abs_error = (abs_error > max_abs_error) ? abs_error : max_abs_error;
00553
00554 sum_sq_abs_error_full += abs_error*abs_error;
00555 sum_sq_prev_voltage_full += prev_voltage[data_point] * prev_voltage[data_point];
00556 if (time_series[this_data_point] <= 8.0)
00557 {
00558
00559 sum_sq_abs_error += abs_error*abs_error;
00560 sum_sq_prev_voltage += prev_voltage[data_point] * prev_voltage[data_point];
00561 }
00562 }
00563
00564 }
00565 if (!this->PopulatedResult || !FixedResult)
00566 {
00567 prev_voltage = transmembrane_potential;
00568 prev_times = time_series;
00569 }
00570
00571 if (this->PopulatedResult)
00572 {
00573 double l2_norm_upstroke = sqrt(sum_sq_abs_error/sum_sq_prev_voltage);
00574 double l2_norm_full = sqrt(sum_sq_abs_error_full/sum_sq_prev_voltage_full);
00575
00576 if (PetscTools::AmMaster())
00577 {
00578 (*p_conv_info_file) << std::setprecision(8)
00579 << Abscissa() << "\t"
00580 << l2_norm_full << "\t"
00581 << l2_norm_upstroke << "\t"
00582 << max_abs_error << "\t"
00583 << Apd90FirstQn <<" "<< apd90_first_qn_error <<""<< "\t"
00584 << Apd90ThirdQn <<" "<< apd90_third_qn_error <<""<< "\t"
00585 << ConductionVelocity <<" "<< cond_velocity_error <<""<< std::endl;
00586 }
00587
00588 this->Converged = l2_norm_full < this->RelativeConvergenceCriterion;
00589 this->LastDifference = l2_norm_full;
00590 #define COVERAGE_IGNORE
00591 assert (time_series.size() != 1u);
00592 if (time_series.back() == 0.0)
00593 {
00594 std::cout << "Failed after successful convergence - give up this convergence test\n";
00595 break;
00596 }
00597 #undef COVERAGE_IGNORE
00598
00599 }
00600
00601 if ( time_series.back() != 0.0)
00602 {
00603
00604 this->PopulatedResult=true;
00605
00606 }
00607 }
00608
00609
00610 if (!this->Converged)
00611 {
00612 UpdateConvergenceParameters();
00613 file_num++;
00614 }
00615 delete p_cell_factory;
00616 }
00617 while (!GiveUpConvergence() && !this->Converged);
00618
00619
00620 if (PetscTools::AmMaster())
00621 {
00622 p_conv_info_file->close();
00623
00624 std::cout << "Results: " << std::endl;
00625 FileFinder info_finder = conv_info_handler.FindFile(nameOfTest + "_info.csv");
00626 std::ifstream info_file(info_finder.GetAbsolutePath().c_str());
00627 if (info_file)
00628 {
00629 std::cout << info_file.rdbuf();
00630 info_file.close();
00631 }
00632 }
00633
00634 }
00635
00636 void DisplayRun()
00637 {
00638 if (!PetscTools::AmMaster())
00639 {
00640 return;
00641 }
00642 unsigned num_ele_across = SmallPow(2u, this->MeshNum+2);
00643 double scaling = mMeshWidth/(double) num_ele_across;
00644
00645 std::cout<<"================================================================================"<<std::endl;
00646 std::cout<<"Solving in " << DIM << "D\t";
00647 std::cout<<"Space step " << scaling << " cm (mesh " << this->MeshNum << ")" << "\n";
00648 std::cout<<"PDE step " << this->PdeTimeStep << " ms" << "\t";
00649 std::cout<<"ODE step " << this->OdeTimeStep << " ms" << "\t";
00650 if (HeartConfig::Instance()->GetUseAbsoluteTolerance())
00651 {
00652 std::cout<<"KSP absolute "<<HeartConfig::Instance()->GetAbsoluteTolerance()<<"\t";
00653 }
00654 else
00655 {
00656 std::cout<<"KSP relative "<<HeartConfig::Instance()->GetRelativeTolerance()<<"\t";
00657 }
00658 switch (this->Stimulus)
00659 {
00660 case PLANE:
00661 std::cout<<"Stimulus = Plane\n";
00662 break;
00663
00664 case QUARTER:
00665 std::cout<<"Stimulus = Ramped first quarter\n";
00666 break;
00667
00668 case NEUMANN:
00669 std::cout<<"Stimulus = Neumann\n";
00670 break;
00671
00672 }
00673
00674 std::time_t rawtime;
00675 std::time( &rawtime );
00676 std::cout << std::ctime(&rawtime);
00679 if (this->UseAbsoluteStimulus)
00680 {
00681 #define COVERAGE_IGNORE
00682 std::cout<<"Using absolute stimulus of "<<this->AbsoluteStimulus<<std::endl;
00683 #undef COVERAGE_IGNORE
00684 }
00685 std::cout << std::flush;
00686
00687
00688
00689 }
00690
00691 public:
00692 virtual ~AbstractConvergenceTester() {}
00693
00695 virtual void SetInitialConvergenceParameters()=0;
00697 virtual void UpdateConvergenceParameters()=0;
00699 virtual bool GiveUpConvergence()=0;
00701 virtual double Abscissa()=0;
00708 virtual void PopulateStandardResult(std::vector<double> &result, std::vector<double> ×)
00709 {
00710 assert(this->PopulatedResult==false);
00711 assert(result.size()==0);
00712 assert(times.size()==0);
00713 }
00714
00718 bool IsConverged()
00719 {
00720 return Converged;
00721 }
00722
00726 void SetMeshWidth(double meshWidth)
00727 {
00728 mMeshWidth=meshWidth;
00729 }
00730 };
00731
00732 #endif