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