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 #include "Warnings.hpp"
00057
00058 typedef enum StimulusType_
00059 {
00060 PLANE=0,
00061 QUARTER,
00062 NEUMANN
00063 } StimulusType;
00064
00069 template <class CELL, unsigned DIM>
00070 class RampedQuarterStimulusCellFactory : public AbstractCardiacCellFactory<DIM>
00071 {
00072 private:
00074 std::vector< boost::shared_ptr<SimpleStimulus> > mpStimuli;
00076 double mMeshWidth;
00078 double mStepSize;
00082 unsigned mLevels;
00083 public:
00084
00091 RampedQuarterStimulusCellFactory(double meshWidth, unsigned numElemAcross, double fullStim=-1e7)
00092 : AbstractCardiacCellFactory<DIM>(),
00093 mMeshWidth(meshWidth),
00094 mStepSize(meshWidth/numElemAcross),
00095 mLevels(numElemAcross/4)
00096 {
00097 assert(numElemAcross%4 == 0);
00098
00099 for (unsigned level=0; level<mLevels; level++)
00100 {
00101 double this_stim = fullStim - (level*fullStim)/mLevels;
00102
00103 mpStimuli.push_back((boost::shared_ptr<SimpleStimulus>)new SimpleStimulus(this_stim, 0.5));
00104 }
00105 }
00106
00107
00113 AbstractCardiacCell* CreateCardiacCellForTissueNode(unsigned node)
00114 {
00115 double x = this->GetMesh()->GetNode(node)->GetPoint()[0];
00116 double d_level = x/mStepSize;
00117 unsigned level = (unsigned) d_level;
00118 assert(fabs(level-d_level) < DBL_MAX);
00119
00120 if (level < mLevels)
00121 {
00122 return new CELL(this->mpSolver, this->mpStimuli[level]);
00123 }
00124 else
00125 {
00126 return new CELL(this->mpSolver, this->mpZeroStimulus);
00127 }
00128 }
00129 };
00130
00131
00136 class AbstractUntemplatedConvergenceTester
00137 {
00138 protected:
00140 double mMeshWidth;
00141 public:
00143 double OdeTimeStep;
00145 double PdeTimeStep;
00150 unsigned MeshNum;
00151 double RelativeConvergenceCriterion;
00152 double LastDifference;
00153 double Apd90FirstQn;
00154 double Apd90ThirdQn;
00155 double ConductionVelocity;
00159 bool PopulatedResult;
00163 bool FixedResult;
00167 bool UseAbsoluteStimulus;
00172 double AbsoluteStimulus;
00173 bool SimulateFullActionPotential;
00174 bool Converged;
00175 StimulusType Stimulus;
00176 double NeumannStimulus;
00178 AbstractUntemplatedConvergenceTester();
00179
00185 virtual void Converge(std::string nameOfTest)=0;
00186
00187 virtual ~AbstractUntemplatedConvergenceTester();
00188 };
00189
00193 template<class CARDIAC_PROBLEM, unsigned DIM>
00194 void SetConductivities(CARDIAC_PROBLEM& rCardiacProblem);
00195
00196 template<unsigned DIM>
00197 void SetConductivities(BidomainProblem<DIM>& rProblem)
00198 {
00199 c_vector<double, DIM> conductivities;
00200 for (unsigned i=0; i<DIM; i++)
00201 {
00202 conductivities[i] = 1.75;
00203 }
00204 HeartConfig::Instance()->SetIntracellularConductivities(conductivities);
00205
00206 for (unsigned i=0; i<DIM; i++)
00207 {
00208 conductivities[i] = 7.0;
00209 }
00210 HeartConfig::Instance()->SetExtracellularConductivities(conductivities);
00211 }
00212
00213 template<unsigned DIM>
00214 void SetConductivities(MonodomainProblem<DIM>& rProblem)
00215 {
00216 c_vector<double, DIM> conductivities;
00217 for (unsigned i=0; i<DIM; i++)
00218 {
00219 conductivities[i] = 1.75;
00220 }
00221 HeartConfig::Instance()->SetIntracellularConductivities(conductivities);
00222 }
00223
00228 template<class CELL, class CARDIAC_PROBLEM, unsigned DIM, unsigned PROBLEM_DIM>
00229 class AbstractConvergenceTester : public AbstractUntemplatedConvergenceTester
00230 {
00231 public:
00236 void Converge(std::string nameOfTest)
00237 {
00238 std::cout << "=========================== Beginning Test...==================================\n";
00239
00240 const std::string mesh_dir = "ConvergenceMesh";
00241 OutputFileHandler output_file_handler(mesh_dir);
00242 ReplicatableVector voltage_replicated;
00243
00244 unsigned file_num=0;
00245
00246
00247 OutputFileHandler conv_info_handler("ConvergencePlots", false);
00248 out_stream p_conv_info_file;
00249 if (PetscTools::AmMaster())
00250 {
00251 p_conv_info_file = conv_info_handler.OpenOutputFile(nameOfTest+"_info.csv");
00252 (*p_conv_info_file) << "#Abcisa\t"
00253 << "l2-norm-full\t"
00254 << "l2-norm-onset\t"
00255 << "Max absolute err\t"
00256 << "APD90_1st_quad\t"
00257 << "APD90_3rd_quad\t"
00258 << "Conduction velocity (relative diffs)" << std::endl;
00259 }
00260 SetInitialConvergenceParameters();
00261
00262 double prev_apd90_first_qn=0.0;
00263 double prev_apd90_third_qn=0.0;
00264 double prev_cond_velocity=0.0;
00265 std::vector<double> prev_voltage;
00266 std::vector<double> prev_times;
00267 PopulateStandardResult(prev_voltage, prev_times);
00268
00269 do
00270 {
00271 CuboidMeshConstructor<DIM> constructor;
00272
00273
00274 double printing_step = this->PdeTimeStep;
00275 #define COVERAGE_IGNORE
00276 while (printing_step < 1.0e-4)
00277 {
00278 printing_step *= 2.0;
00279 std::cout<<"Warning: PrintingTimeStep increased\n";
00280 }
00281 #undef COVERAGE_IGNORE
00282 HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(this->OdeTimeStep, this->PdeTimeStep, printing_step);
00283 #define COVERAGE_IGNORE
00284 if (SimulateFullActionPotential)
00285 {
00286 HeartConfig::Instance()->SetSimulationDuration(400.0);
00287 }
00288 else
00289 {
00290 HeartConfig::Instance()->SetSimulationDuration(8.0);
00291 }
00292 #undef COVERAGE_IGNORE
00293 HeartConfig::Instance()->SetOutputDirectory("Convergence");
00294 HeartConfig::Instance()->SetOutputFilenamePrefix("Results");
00295
00296 DistributedTetrahedralMesh<DIM, DIM> mesh;
00297 constructor.Construct(mesh, this->MeshNum, mMeshWidth);
00298
00299 unsigned num_ele_across = (unsigned) SmallPow(2, this->MeshNum+2);
00300
00301 AbstractCardiacCellFactory<DIM>* p_cell_factory=NULL;
00302
00303 switch (this->Stimulus)
00304 {
00305 case NEUMANN:
00306 {
00307 p_cell_factory = new ZeroStimulusCellFactory<CELL, DIM>();
00308 break;
00309 }
00310 case PLANE:
00311 {
00312 if (this->UseAbsoluteStimulus)
00313 {
00314 #define COVERAGE_IGNORE
00315 p_cell_factory = new GeneralPlaneStimulusCellFactory<CELL, DIM>(0, this->AbsoluteStimulus, true);
00316 #undef COVERAGE_IGNORE
00317 }
00318 else
00319 {
00320 p_cell_factory = new GeneralPlaneStimulusCellFactory<CELL, DIM>(num_ele_across, constructor.GetWidth(), false, this->AbsoluteStimulus);
00321 }
00322 break;
00323 }
00324 case QUARTER:
00325 {
00327 p_cell_factory = new RampedQuarterStimulusCellFactory<CELL, DIM>(constructor.GetWidth(), num_ele_across, this->AbsoluteStimulus/10.0);
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];
00399 if (x*x<=1e-10)
00400 {
00401 p_bcc->AddNeumannBoundaryCondition(*iter, p_bc_stim);
00402 }
00403 iter++;
00404 }
00405
00406 cardiac_problem.SetBoundaryConditionsContainer(p_bcc);
00407 }
00408
00409 DisplayRun();
00410 double time_before=MPI_Wtime();
00412
00413
00414 try
00415 {
00416 cardiac_problem.Solve();
00417 }
00418 catch (Exception e)
00419 {
00420 WARNING("This run threw an exception. Check convergence results\n");
00421 std::cout << e.GetMessage() << std::endl;
00422 }
00423
00424 std::cout << "Time to solve = " << MPI_Wtime()-time_before << " seconds\n";
00425
00426 OutputFileHandler results_handler("Convergence", false);
00427 Hdf5DataReader results_reader = cardiac_problem.GetDataReader();
00428
00429 {
00430 std::vector<double> transmembrane_potential = results_reader.GetVariableOverTime("V", third_quadrant_node);
00431 std::vector<double> time_series = results_reader.GetUnlimitedDimensionValues();
00432
00433 OutputFileHandler plot_file_handler("ConvergencePlots", false);
00434 if (PetscTools::AmMaster())
00435 {
00436
00437 {
00438 std::stringstream plot_file_name_stream;
00439 plot_file_name_stream<< nameOfTest << "_Third_quadrant_node_run_"<< file_num << ".csv";
00440 out_stream p_plot_file = plot_file_handler.OpenOutputFile(plot_file_name_stream.str());
00441 for (unsigned data_point = 0; data_point<time_series.size(); data_point++)
00442 {
00443 (*p_plot_file) << time_series[data_point] << "\t" << transmembrane_potential[data_point] << "\n";
00444 }
00445 p_plot_file->close();
00446 }
00447
00448 {
00449 std::vector<double> transmembrane_potential_1qd=results_reader.GetVariableOverTime("V", first_quadrant_node);
00450 std::vector<double> time_series_1qd = results_reader.GetUnlimitedDimensionValues();
00451 std::stringstream plot_file_name_stream;
00452 plot_file_name_stream<< nameOfTest << "_First_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_1qd[data_point] << "\t" << transmembrane_potential_1qd[data_point] << "\n";
00457 }
00458 p_plot_file->close();
00459 }
00460 }
00461
00462 PropagationPropertiesCalculator ppc(&results_reader);
00463
00464 try
00465 {
00466 #define COVERAGE_IGNORE
00467 if (SimulateFullActionPotential)
00468 {
00469 Apd90FirstQn = ppc.CalculateActionPotentialDuration(90.0, first_quadrant_node);
00470 Apd90ThirdQn = ppc.CalculateActionPotentialDuration(90.0, third_quadrant_node);
00471 }
00472 #undef COVERAGE_IGNORE
00473 ConductionVelocity = ppc.CalculateConductionVelocity(first_quadrant_node,third_quadrant_node,0.5*mesh_width);
00474 }
00475 catch (Exception e)
00476 {
00477 #define COVERAGE_IGNORE
00478 std::cout << "Warning - this run threw an exception in calculating propagation. Check convergence results\n";
00479 std::cout << e.GetMessage() << std::endl;
00480 #undef COVERAGE_IGNORE
00481 }
00482 double cond_velocity_error = 1e10;
00483 double apd90_first_qn_error = 1e10;
00484 double apd90_third_qn_error = 1e10;
00485
00486 if (this->PopulatedResult)
00487 {
00488 if (prev_cond_velocity != 0.0)
00489 {
00490 cond_velocity_error = fabs(ConductionVelocity - prev_cond_velocity) / prev_cond_velocity;
00491 }
00492 #define COVERAGE_IGNORE
00493 if (prev_apd90_first_qn != 0.0)
00494 {
00495 apd90_first_qn_error = fabs(Apd90FirstQn - prev_apd90_first_qn) / prev_apd90_first_qn;
00496 }
00497 if (prev_apd90_third_qn != 0.0)
00498 {
00499 apd90_third_qn_error = fabs(Apd90ThirdQn - prev_apd90_third_qn) / prev_apd90_third_qn;
00500 }
00501 if (apd90_first_qn_error == 0.0)
00502 {
00503 apd90_first_qn_error = DBL_EPSILON;
00504 }
00505 if (apd90_third_qn_error == 0.0)
00506 {
00507 apd90_third_qn_error = DBL_EPSILON;
00508 }
00509 #undef COVERAGE_IGNORE
00510 if (cond_velocity_error == 0.0)
00511 {
00512 cond_velocity_error = DBL_EPSILON;
00513 }
00514 }
00515
00516 prev_cond_velocity = ConductionVelocity;
00517 prev_apd90_first_qn = Apd90FirstQn;
00518 prev_apd90_third_qn = Apd90ThirdQn;
00519
00520
00521 double max_abs_error = 0;
00522 double sum_sq_abs_error =0;
00523 double sum_sq_prev_voltage = 0;
00524 double sum_sq_abs_error_full =0;
00525 double sum_sq_prev_voltage_full = 0;
00526 if (this->PopulatedResult)
00527 {
00528
00529
00530 unsigned time_factor=(time_series.size()-1) / (prev_times.size()-1);
00531 assert (time_factor == 1 || time_factor == 2 || time_factor == 8);
00532
00533 for (unsigned data_point = 0; data_point<prev_times.size(); data_point++)
00534 {
00535 unsigned this_data_point=time_factor*data_point;
00536
00537 assert(time_series[this_data_point] == prev_times[data_point]);
00538 double abs_error = fabs(transmembrane_potential[this_data_point]-prev_voltage[data_point]);
00539 max_abs_error = (abs_error > max_abs_error) ? abs_error : max_abs_error;
00540
00541 sum_sq_abs_error_full += abs_error*abs_error;
00542 sum_sq_prev_voltage_full += prev_voltage[data_point] * prev_voltage[data_point];
00543 if (time_series[this_data_point] <= 8.0)
00544 {
00545
00546 sum_sq_abs_error += abs_error*abs_error;
00547 sum_sq_prev_voltage += prev_voltage[data_point] * prev_voltage[data_point];
00548 }
00549 }
00550
00551 }
00552 if (!this->PopulatedResult || !FixedResult)
00553 {
00554 prev_voltage = transmembrane_potential;
00555 prev_times = time_series;
00556 }
00557
00558 if (this->PopulatedResult)
00559 {
00560
00561 if (PetscTools::AmMaster())
00562 {
00563 (*p_conv_info_file) << std::setprecision(8)
00564 << Abscissa() << "\t"
00565 << sqrt(sum_sq_abs_error_full/sum_sq_prev_voltage_full) << "\t"
00566 << sqrt(sum_sq_abs_error/sum_sq_prev_voltage) << "\t"
00567 << max_abs_error << "\t"
00568 << Apd90FirstQn <<" "<< apd90_first_qn_error <<""<< "\t"
00569 << Apd90ThirdQn <<" "<< apd90_third_qn_error <<""<< "\t"
00570 << ConductionVelocity <<" "<< cond_velocity_error <<""<< std::endl;
00571 }
00572
00573 this->Converged = sum_sq_abs_error/sum_sq_prev_voltage<this->RelativeConvergenceCriterion;
00574 this->LastDifference=sum_sq_abs_error/sum_sq_prev_voltage;
00575 #define COVERAGE_IGNORE
00576 if (time_series.size() == 1u)
00577 {
00578 std::cout << "Failed after successful convergence - give up this convergence test\n";
00579 break;
00580 }
00581 #undef COVERAGE_IGNORE
00582
00583 }
00584
00585 if (!this->PopulatedResult)
00586 {
00587 this->PopulatedResult=true;
00588
00589 }
00590 }
00591
00592
00593 if (!this->Converged)
00594 {
00595 UpdateConvergenceParameters();
00596 file_num++;
00597 }
00598 delete p_cell_factory;
00599 }
00600 while (!GiveUpConvergence() && !this->Converged);
00601
00602
00603 if (PetscTools::AmMaster())
00604 {
00605 p_conv_info_file->close();
00606
00607 std::cout << "Results: " << std::endl;
00608 ABORT_IF_NON0(system, "cat " + conv_info_handler.GetOutputDirectoryFullPath() + nameOfTest + "_info.csv");
00609 }
00610
00611 }
00612
00613 void DisplayRun()
00614 {
00615 unsigned num_ele_across = (unsigned) SmallPow(2, this->MeshNum+2);
00616 double scaling = mMeshWidth/(double) num_ele_across;
00617
00618 std::cout<<"================================================================================"<<std::endl;
00619 std::cout<<"Solving in "<<DIM<<"D\t";
00620 std::cout<<"Space step "<< scaling << " cm (mesh " << this->MeshNum << ")" << "\n";
00621 std::cout<<"PDE step "<<this->PdeTimeStep<<" ms"<<"\t";
00622 std::cout<<"ODE step "<<this->OdeTimeStep<<" ms"<<"\t";
00623 if (HeartConfig::Instance()->GetUseAbsoluteTolerance())
00624 {
00625 std::cout<<"KSP absolute "<<HeartConfig::Instance()->GetAbsoluteTolerance()<<"\t";
00626 }
00627 else
00628 {
00629 std::cout<<"KSP relative "<<HeartConfig::Instance()->GetRelativeTolerance()<<"\t";
00630 }
00631 switch (this->Stimulus)
00632 {
00633 case PLANE:
00634 std::cout<<"Stimulus = Plane\n";
00635 break;
00636
00637 case QUARTER:
00638 std::cout<<"Stimulus = Ramped first quarter\n";
00639 break;
00640
00641 case NEUMANN:
00642 std::cout<<"Stimulus = Neumann\n";
00643 break;
00644
00645 }
00646 EXPECT0(system, "date");
00649 if (this->UseAbsoluteStimulus)
00650 {
00651 #define COVERAGE_IGNORE
00652 std::cout<<"Using absolute stimulus of "<<this->AbsoluteStimulus<<std::endl;
00653 #undef COVERAGE_IGNORE
00654 }
00655 std::cout << std::flush;
00656
00657
00658
00659 }
00660
00661 public:
00662 virtual ~AbstractConvergenceTester() {}
00663
00665 virtual void SetInitialConvergenceParameters()=0;
00667 virtual void UpdateConvergenceParameters()=0;
00669 virtual bool GiveUpConvergence()=0;
00671 virtual double Abscissa()=0;
00678 virtual void PopulateStandardResult(std::vector<double> &result, std::vector<double> ×)
00679 {
00680 assert(this->PopulatedResult==false);
00681 assert(result.size()==0);
00682 assert(times.size()==0);
00683 }
00684
00688 bool IsConverged()
00689 {
00690 return Converged;
00691 }
00692
00696 void SetMeshWidth(double meshWidth)
00697 {
00698 mMeshWidth=meshWidth;
00699 }
00700 };
00701
00702 #endif