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 <math.h>
00040
00041 #include "AbstractMesh.hpp"
00042 #include "TetrahedralMesh.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 "OutputFileHandler.hpp"
00052 #include "ZeroStimulusCellFactory.hpp"
00053 #include "SimpleStimulus.hpp"
00054 #include "ConstBoundaryCondition.hpp"
00055 #include "StimulusBoundaryCondition.hpp"
00056
00057
00058 typedef enum StimulusType_
00059 {
00060 PLANE=0,
00061 REGION,
00062 NEUMANN
00063 } StimulusType;
00064
00068 template <class CELL, unsigned DIM>
00069 class QuarterStimulusCellFactory : public AbstractCardiacCellFactory<DIM>
00070 {
00071 private:
00072
00073 SimpleStimulus* mpStimulus;
00074 double mMeshWidth;
00075 public:
00076 QuarterStimulusCellFactory(double meshWidth) : AbstractCardiacCellFactory<DIM>()
00077 {
00078 mpStimulus = new SimpleStimulus(-1000000, 0.5);
00079 mMeshWidth=meshWidth;
00080 }
00081
00082 AbstractCardiacCell* CreateCardiacCellForTissueNode(unsigned node)
00083 {
00084 double x = this->mpMesh->GetNode(node)->GetPoint()[0];
00085 if (x<=mMeshWidth*0.25+1e-10)
00086 {
00087 return new CELL(this->mpSolver, this->mpStimulus);
00088 }
00089 else
00090 {
00091 return new CELL(this->mpSolver, this->mpZeroStimulus);
00092 }
00093 }
00094
00095 ~QuarterStimulusCellFactory(void)
00096 {
00097 delete mpStimulus;
00098 }
00099 };
00100
00101
00105 class AbstractUntemplatedConvergenceTester
00106 {
00107 protected:
00108 double mMeshWidth;
00109 double mKspTolerance;
00110 bool mUseKspAbsoluteTolerance;
00111 public:
00112 double OdeTimeStep;
00113 double PdeTimeStep;
00114 unsigned MeshNum;
00115 double RelativeConvergenceCriterion;
00116 double LastDifference;
00117 double AbsoluteStimulus;
00118 bool PopulatedResult;
00119 bool FixedResult;
00120 bool UseAbsoluteStimulus;
00121
00122 bool Converged;
00123
00124 StimulusType Stimulus;
00125 double NeumannStimulus;
00126
00127 AbstractUntemplatedConvergenceTester();
00128
00129 virtual void Converge(std::string nameOfTest)=0;
00130
00131 void SetKspRelativeTolerance(const double relativeTolerance);
00132
00133 void SetKspAbsoluteTolerance(const double absoluteTolerance);
00134
00135 double GetKspAbsoluteTolerance();
00136
00137 double GetKspRelativeTolerance();
00138
00139 virtual ~AbstractUntemplatedConvergenceTester();
00140 };
00141
00145 template<class CARDIAC_PROBLEM, unsigned DIM>
00146 void SetConductivities(CARDIAC_PROBLEM& rCardiacProblem);
00147
00148 template<unsigned DIM>
00149 void SetConductivities(BidomainProblem<DIM>& rProblem)
00150 {
00151 c_vector<double, DIM> conductivities;
00152 for (unsigned i=0; i<DIM; i++)
00153 {
00154 conductivities[i] = 1.75;
00155 }
00156 HeartConfig::Instance()->SetIntracellularConductivities(conductivities);
00157
00158 for (unsigned i=0; i<DIM; i++)
00159 {
00160 conductivities[i] = 7.0;
00161 }
00162 HeartConfig::Instance()->SetExtracellularConductivities(conductivities);
00163 }
00164
00165 template<unsigned DIM>
00166 void SetConductivities(MonodomainProblem<DIM>& rProblem)
00167 {
00168 c_vector<double, DIM> conductivities;
00169 for (unsigned i=0; i<DIM; i++)
00170 {
00171 conductivities[i] = 1.75;
00172 }
00173 HeartConfig::Instance()->SetIntracellularConductivities(conductivities);
00174 }
00175
00176
00177
00181 template<class CELL, class CARDIAC_PROBLEM, unsigned DIM, unsigned PROBLEM_DIM>
00182 class AbstractConvergenceTester : public AbstractUntemplatedConvergenceTester
00183 {
00184 public:
00188 void Converge(std::string nameOfTest)
00189 {
00190 std::cout << "=========================== Beginning Test...==================================\n";
00191
00192 const std::string mesh_dir = "ConvergenceMesh";
00193 OutputFileHandler output_file_handler(mesh_dir);
00194 ReplicatableVector voltage_replicated;
00195
00196 unsigned file_num=0;
00197
00198
00199 OutputFileHandler conv_info_handler("ConvergencePlots", false);
00200 out_stream p_conv_info_file;
00201 if (conv_info_handler.IsMaster())
00202 {
00203 p_conv_info_file = conv_info_handler.OpenOutputFile(nameOfTest+"_info.csv");
00204 (*p_conv_info_file) << "#Abcisa\t"
00205 << "(l2-norm)^2\t"
00206 << "l2-norm\t"
00207 << "Max absolute err\t"
00208 << "APD90_1st_quad\t"
00209 << "APD90_3rd_quad\t"
00210 << "Conduction velocity (relative errors)" << std::endl;
00211 }
00212 SetInitialConvergenceParameters();
00213
00214 double prev_apd90_first_qn=0.0;
00215 double prev_apd90_third_qn=0.0;
00216 double prev_cond_velocity=0.0;
00217 double prev_voltage[201];
00218 PopulateStandardResult(prev_voltage);
00219
00220 do
00221 {
00222 CuboidMeshConstructor<DIM> constructor;
00223
00224 assert(fabs(0.04/this->PdeTimeStep - round(0.04/this->PdeTimeStep)) <1e-15 );
00225
00226 HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(this->OdeTimeStep, this->PdeTimeStep, 0.04);
00227 HeartConfig::Instance()->SetSimulationDuration(8.0);
00228 HeartConfig::Instance()->SetOutputDirectory ("Convergence");
00229 HeartConfig::Instance()->SetOutputFilenamePrefix ("Results");
00230
00231
00232
00233
00234 TetrahedralMesh<DIM, DIM> mesh;
00235 TrianglesMeshReader<DIM, DIM> mesh_reader(constructor.Construct(this->MeshNum, mMeshWidth) );
00236 mesh.ConstructFromMeshReader(mesh_reader);
00237
00238 unsigned num_ele_across = (unsigned) pow(2, this->MeshNum+2);
00239
00240 AbstractCardiacCellFactory<DIM>* p_cell_factory=NULL;
00241
00242 switch (this->Stimulus)
00243 {
00244 case NEUMANN:
00245 {
00246 p_cell_factory = new ZeroStimulusCellFactory<CELL, DIM>();
00247 break;
00248 }
00249 case PLANE:
00250 {
00251 if (this->UseAbsoluteStimulus)
00252 {
00253 #define COVERAGE_IGNORE
00254 p_cell_factory = new GeneralPlaneStimulusCellFactory<CELL, DIM>(0, this->AbsoluteStimulus, true);
00255 #undef COVERAGE_IGNORE
00256 }
00257 else
00258 {
00259 p_cell_factory = new GeneralPlaneStimulusCellFactory<CELL, DIM>(num_ele_across, constructor.GetWidth());
00260 }
00261 break;
00262 }
00263 case REGION:
00264 {
00265 p_cell_factory = new QuarterStimulusCellFactory<CELL, DIM>(constructor.GetWidth());
00266 break;
00267 }
00268 }
00269
00270
00271 CARDIAC_PROBLEM cardiac_problem(p_cell_factory);
00273 cardiac_problem.SetMesh(&mesh);
00274 if (mUseKspAbsoluteTolerance)
00275 {
00276 HeartConfig::Instance()->SetUseAbsoluteTolerance(this->mKspTolerance);
00277
00278
00279 }
00280 else
00281 {
00282 HeartConfig::Instance()->SetUseRelativeTolerance(this->mKspTolerance);
00283
00284
00285 }
00286
00287
00288 unsigned third_quadrant_node;
00289 unsigned first_quadrant_node;
00290 switch(DIM)
00291 {
00292 case 1:
00293 {
00294 first_quadrant_node = (unsigned) (0.25*constructor.NumElements);
00295 third_quadrant_node = (unsigned) (0.75*constructor.NumElements);
00296 break;
00297 }
00298 case 2:
00299 {
00300 unsigned n= (unsigned) pow (2, this->MeshNum+2);
00301 first_quadrant_node = (n+1)*(n/2)+ n/4 ;
00302 third_quadrant_node = (n+1)*(n/2)+3*n/4 ;
00303 break;
00304 }
00305 case 3:
00306 {
00307 const unsigned first_quadrant_nodes_3d[5]={61, 362, 2452, 17960, 137296};
00308 const unsigned third_quadrant_nodes_3d[5]={63, 366, 2460, 17976, 137328};
00309 assert(this->PdeTimeStep<5);
00310 first_quadrant_node = first_quadrant_nodes_3d[this->MeshNum];
00311 third_quadrant_node = third_quadrant_nodes_3d[this->MeshNum];
00312 break;
00313 }
00314
00315 default:
00316 assert(0);
00317 }
00318
00319 double mesh_width=constructor.GetWidth();
00320
00321
00322 std::vector<unsigned> nodes_to_be_output;
00323 nodes_to_be_output.push_back(first_quadrant_node);
00324 nodes_to_be_output.push_back(third_quadrant_node);
00325 cardiac_problem.SetOutputNodes(nodes_to_be_output);
00327
00328
00329
00330
00331 SetConductivities(cardiac_problem);
00332
00333 cardiac_problem.Initialise();
00334
00335 #ifndef NDEBUG
00336 Node<DIM>* fqn = cardiac_problem.rGetMesh().GetNode(first_quadrant_node);
00337 Node<DIM>* tqn = cardiac_problem.rGetMesh().GetNode(third_quadrant_node);
00338 assert(fqn->rGetLocation()[0]==0.25*mesh_width);
00339 assert(fabs(tqn->rGetLocation()[0] - 0.75*mesh_width) < 1e-10);
00340 for (unsigned coord=1; coord<DIM; coord++)
00341 {
00342 assert(fqn->rGetLocation()[coord]==0.5*mesh_width);
00343 assert(tqn->rGetLocation()[coord]==0.5*mesh_width);
00344 }
00345 #endif
00346
00347 BoundaryConditionsContainer<DIM,DIM,PROBLEM_DIM> bcc;
00348 SimpleStimulus stim(NeumannStimulus, 0.5);
00349 if (Stimulus==NEUMANN)
00350 {
00351
00352 StimulusBoundaryCondition<DIM> *p_bc_stim = new StimulusBoundaryCondition<DIM>(&stim);
00353
00354
00355 AbstractMesh<DIM, DIM> &r_mesh = cardiac_problem.rGetMesh();
00356
00357 typename AbstractMesh<DIM, DIM>::BoundaryElementIterator iter;
00358 iter = r_mesh.GetBoundaryElementIteratorBegin();
00359 while (iter != r_mesh.GetBoundaryElementIteratorEnd())
00360 {
00361 double x = ((*iter)->CalculateCentroid())[0];
00362 if (x*x<=1e-10)
00363 {
00364 bcc.AddNeumannBoundaryCondition(*iter, p_bc_stim);
00365 }
00366 iter++;
00367 }
00368
00369 cardiac_problem.SetBoundaryConditionsContainer(&bcc);
00370 }
00371
00372 DisplayRun();
00373 double time_before=MPI_Wtime();
00375
00376
00377 try
00378 {
00379 cardiac_problem.Solve();
00380 }
00381 catch (Exception e)
00382 {
00383 #define COVERAGE_IGNORE
00385 std::cout<<"Warning - this run threw an exception. Check convergence results\n";
00386 std::cout<<e.GetMessage() << std::endl;
00387 #undef COVERAGE_IGNORE
00388 }
00389
00390 std::cout << "Time to solve = "<<MPI_Wtime()-time_before<<" seconds\n";
00391
00392 OutputFileHandler results_handler("Convergence", false);
00393 Hdf5DataReader results_reader = cardiac_problem.GetDataReader();
00394
00395 {
00396 std::vector<double> transmembrane_potential=results_reader.GetVariableOverTime("V", third_quadrant_node);
00397 std::vector<double> time_series = results_reader.GetUnlimitedDimensionValues();
00398
00399
00400 if (results_handler.IsMaster())
00401 {
00402 OutputFileHandler plot_file_handler("ConvergencePlots", false);
00403 std::stringstream plot_file_name_stream;
00404 plot_file_name_stream<< nameOfTest << "_Third_quadrant_node_run_"<< file_num << ".csv";
00405 out_stream p_plot_file = plot_file_handler.OpenOutputFile(plot_file_name_stream.str());
00406 for (unsigned data_point = 0; data_point<time_series.size(); data_point++)
00407 {
00408 (*p_plot_file) << time_series[data_point] << "\t" << transmembrane_potential[data_point] << "\n";
00409 }
00410 p_plot_file->close();
00411 }
00412
00413
00414 if (results_handler.IsMaster())
00415 {
00416 std::vector<double> transmembrane_potential_1qd=results_reader.GetVariableOverTime("V", first_quadrant_node);
00417 std::vector<double> time_series_1qd = results_reader.GetUnlimitedDimensionValues();
00418 OutputFileHandler plot_file_handler("ConvergencePlots", false);
00419 std::stringstream plot_file_name_stream;
00420 plot_file_name_stream<< nameOfTest << "_First_quadrant_node_run_"<< file_num << ".csv";
00421 out_stream p_plot_file = plot_file_handler.OpenOutputFile(plot_file_name_stream.str());
00422 for (unsigned data_point = 0; data_point<time_series.size(); data_point++)
00423 {
00424 (*p_plot_file) << time_series_1qd[data_point] << "\t" << transmembrane_potential_1qd[data_point] << "\n";
00425 }
00426 p_plot_file->close();
00427 }
00428
00429
00430 PropagationPropertiesCalculator ppc(&results_reader);
00431
00432 double cond_velocity=0.0, apd90_first_qn=0.0, apd90_third_qn=0.0;
00433 try
00434 {
00435 apd90_first_qn = ppc.CalculateActionPotentialDuration(0.9, first_quadrant_node);
00436 apd90_third_qn = ppc.CalculateActionPotentialDuration(0.9, third_quadrant_node);
00437
00438 cond_velocity = ppc.CalculateConductionVelocity(first_quadrant_node,third_quadrant_node,0.5*mesh_width);
00439 }
00440 catch (Exception e)
00441 {
00442 #define COVERAGE_IGNORE
00443 std::cout<<"Warning - this run threw an exception in calculating propagation. Check convergence results\n";
00444 std::cout<<e.GetMessage() << std::endl;
00445 #undef COVERAGE_IGNORE
00446 }
00447
00448 double cond_velocity_error = 0.0;
00449 double apd90_first_qn_error = 0.0;
00450 double apd90_third_qn_error = 0.0;
00451
00452 if (this->PopulatedResult)
00453 {
00454
00455
00456
00457
00458 cond_velocity_error = fabs(cond_velocity - prev_cond_velocity) / prev_cond_velocity;
00459 apd90_first_qn_error = fabs(apd90_first_qn - prev_apd90_first_qn) / prev_apd90_first_qn;
00460 apd90_third_qn_error = fabs(apd90_third_qn - prev_apd90_third_qn) / prev_apd90_third_qn;
00461 }
00462
00463 prev_cond_velocity = cond_velocity;
00464 prev_apd90_first_qn = apd90_first_qn;
00465 prev_apd90_third_qn = apd90_third_qn;
00466
00467
00468 double max_abs_error = 0;
00469 double sum_sq_abs_error =0;
00470 double sum_sq_prev_voltage = 0;
00471
00472 for (unsigned data_point = 0; data_point<time_series.size(); data_point++)
00473 {
00474 if (this->PopulatedResult)
00475 {
00476 double abs_error = fabs(transmembrane_potential[data_point]-prev_voltage[data_point]);
00477 max_abs_error = (abs_error > max_abs_error) ? abs_error : max_abs_error;
00478 sum_sq_abs_error += abs_error*abs_error;
00479 sum_sq_prev_voltage += prev_voltage[data_point] * prev_voltage[data_point];
00480 }
00481
00482 if (!this->PopulatedResult || !FixedResult)
00483 {
00484 prev_voltage[data_point] = transmembrane_potential[data_point];
00485 }
00486 }
00487
00488 if (this->PopulatedResult)
00489 {
00490
00491 std::cout << "max_abs_error = " << max_abs_error << " log10 = " << log10(max_abs_error) << "\n";
00492 std::cout << "l2 error = " << sum_sq_abs_error/sum_sq_prev_voltage << " log10 = " << log10(sum_sq_abs_error/sum_sq_prev_voltage) << "\n";
00493
00494
00495
00496
00497 if (conv_info_handler.IsMaster())
00498 {
00499 (*p_conv_info_file) << std::setprecision(8)
00500 << Abscissa() << "\t"
00501 << sum_sq_abs_error/sum_sq_prev_voltage << "\t"
00502 << sqrt(sum_sq_abs_error/sum_sq_prev_voltage) << "\t"
00503 << max_abs_error << "\t"
00504 << apd90_first_qn_error << "\t"
00505 << apd90_third_qn_error << "\t"
00506 << cond_velocity_error << std::endl;
00507 }
00508
00509 this->Converged = sum_sq_abs_error/sum_sq_prev_voltage<this->RelativeConvergenceCriterion;
00510 this->LastDifference=sum_sq_abs_error/sum_sq_prev_voltage;
00511 }
00512
00513 if (!this->PopulatedResult)
00514 {
00515 this->PopulatedResult=true;
00516
00517 }
00518 }
00519
00520
00521 if (!this->Converged)
00522 {
00523 UpdateConvergenceParameters();
00524 file_num++;
00525 }
00526 delete p_cell_factory;
00527 }
00528 while (!GiveUpConvergence() && !this->Converged);
00529
00530 if (conv_info_handler.IsMaster())
00531 {
00532 p_conv_info_file->close();
00533
00534 std::cout << "Results: " << std::endl;
00535 EXPECT0(system, "cat " + conv_info_handler.GetOutputDirectoryFullPath() + nameOfTest + "_info.csv");
00536 }
00537
00538 }
00539
00540 void DisplayRun()
00541 {
00542 unsigned num_ele_across = (unsigned) pow(2, this->MeshNum+2);
00543 double scaling = mMeshWidth/(double) num_ele_across;
00544
00545 std::cout<<"================================================================================"<<std::endl;
00546 std::cout<<"Solving in "<<DIM<<"D\n";
00547 std::cout<<"Solving with a space step of "<< scaling << " cm (mesh " << this->MeshNum << ")" << std::endl;
00548 std::cout<<"Solving with a time step of "<<this->PdeTimeStep<<" ms"<<std::endl;
00549 std::cout<<"Solving with an ode time step of "<<this->OdeTimeStep<<" ms"<<std::endl;
00550 if (mUseKspAbsoluteTolerance)
00551 {
00552 std::cout<<"Solving with a KSP absolute tolerance of "<<this->mKspTolerance<<std::endl;
00553 }
00554 else
00555 {
00556 std::cout<<"Solving with a KSP relative tolerance of "<<this->mKspTolerance<<std::endl;
00557 }
00558 switch (this->Stimulus)
00559 {
00560 case PLANE:
00561 std::cout<<"Stimulus = Plane\n";
00562 break;
00563
00564 case REGION:
00565 std::cout<<"Stimulus = Region\n";
00566 break;
00567
00568 case NEUMANN:
00569 std::cout<<"Stimulus = Neumann\n";
00570 break;
00571
00572 }
00573 EXPECT0(system, "date");
00576 if (this->UseAbsoluteStimulus)
00577 {
00578 #define COVERAGE_IGNORE
00579 std::cout<<"Using absolute stimulus of "<<this->AbsoluteStimulus<<std::endl;
00580 #undef COVERAGE_IGNORE
00581 }
00582 std::cout << std::flush;
00583
00584 }
00585
00586 public:
00587 virtual ~AbstractConvergenceTester() {}
00588
00589 virtual void SetInitialConvergenceParameters()=0;
00590 virtual void UpdateConvergenceParameters()=0;
00591 virtual bool GiveUpConvergence()=0;
00592 virtual double Abscissa()=0;
00593 virtual void PopulateStandardResult(double result[])
00594 {
00595 assert(this->PopulatedResult==false);
00596 }
00597
00598 bool IsConverged()
00599 {
00600 return Converged;
00601 }
00602
00603 void SetMeshWidth(double meshWidth)
00604 {
00605 mMeshWidth=meshWidth;
00606 }
00607 };
00608
00609 #endif