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