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