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 "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
00069 template <class CELL, unsigned DIM>
00070 class QuarterStimulusCellFactory : public AbstractCardiacCellFactory<DIM>
00071 {
00072 private:
00074 boost::shared_ptr<SimpleStimulus> mpStimulus;
00076 double mMeshWidth;
00077 public:
00078
00083 QuarterStimulusCellFactory(double meshWidth)
00084 : AbstractCardiacCellFactory<DIM>(),
00085 mpStimulus(new SimpleStimulus(-1000000, 0.5)),
00086 mMeshWidth(meshWidth)
00087 {
00088 }
00089
00100 AbstractCardiacCell* CreateCardiacCellForTissueNode(unsigned node)
00101 {
00102 double x = this->GetMesh()->GetNode(node)->GetPoint()[0];
00103 if (x<=mMeshWidth*0.25+1e-10)
00104 {
00105 return new CELL(this->mpSolver, this->mpStimulus);
00106 }
00107 else
00108 {
00109 return new CELL(this->mpSolver, this->mpZeroStimulus);
00110 }
00111 }
00112 };
00113
00114
00119 class AbstractUntemplatedConvergenceTester
00120 {
00121 protected:
00123 double mMeshWidth;
00124 public:
00126 double OdeTimeStep;
00128 double PdeTimeStep;
00133 unsigned MeshNum;
00134 double RelativeConvergenceCriterion;
00135 double LastDifference;
00136 double Apd90FirstQn;
00137 double Apd90ThirdQn;
00138 double ConductionVelocity;
00142 bool PopulatedResult;
00146 bool FixedResult;
00150 bool UseAbsoluteStimulus;
00155 double AbsoluteStimulus;
00156 bool SimulateFullActionPotential;
00157 bool Converged;
00158 StimulusType Stimulus;
00159 double NeumannStimulus;
00161 AbstractUntemplatedConvergenceTester();
00162
00168 virtual void Converge(std::string nameOfTest)=0;
00169
00170 virtual ~AbstractUntemplatedConvergenceTester();
00171 };
00172
00176 template<class CARDIAC_PROBLEM, unsigned DIM>
00177 void SetConductivities(CARDIAC_PROBLEM& rCardiacProblem);
00178
00179 template<unsigned DIM>
00180 void SetConductivities(BidomainProblem<DIM>& rProblem)
00181 {
00182 c_vector<double, DIM> conductivities;
00183 for (unsigned i=0; i<DIM; i++)
00184 {
00185 conductivities[i] = 1.75;
00186 }
00187 HeartConfig::Instance()->SetIntracellularConductivities(conductivities);
00188
00189 for (unsigned i=0; i<DIM; i++)
00190 {
00191 conductivities[i] = 7.0;
00192 }
00193 HeartConfig::Instance()->SetExtracellularConductivities(conductivities);
00194 }
00195
00196 template<unsigned DIM>
00197 void SetConductivities(MonodomainProblem<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
00211 template<class CELL, class CARDIAC_PROBLEM, unsigned DIM, unsigned PROBLEM_DIM>
00212 class AbstractConvergenceTester : public AbstractUntemplatedConvergenceTester
00213 {
00214 public:
00219 void Converge(std::string nameOfTest)
00220 {
00221 std::cout << "=========================== Beginning Test...==================================\n";
00222
00223 const std::string mesh_dir = "ConvergenceMesh";
00224 OutputFileHandler output_file_handler(mesh_dir);
00225 ReplicatableVector voltage_replicated;
00226
00227 unsigned file_num=0;
00228
00229
00230 OutputFileHandler conv_info_handler("ConvergencePlots", false);
00231 out_stream p_conv_info_file;
00232 if (conv_info_handler.IsMaster())
00233 {
00234 p_conv_info_file = conv_info_handler.OpenOutputFile(nameOfTest+"_info.csv");
00235 (*p_conv_info_file) << "#Abcisa\t"
00236 << "l2-norm-full\t"
00237 << "l2-norm-onset\t"
00238 << "Max absolute err\t"
00239 << "APD90_1st_quad\t"
00240 << "APD90_3rd_quad\t"
00241 << "Conduction velocity (relative diffs)" << std::endl;
00242 }
00243 SetInitialConvergenceParameters();
00244
00245 double prev_apd90_first_qn=0.0;
00246 double prev_apd90_third_qn=0.0;
00247 double prev_cond_velocity=0.0;
00248 std::vector<double> prev_voltage;
00249 std::vector<double> prev_times;
00250 PopulateStandardResult(prev_voltage, prev_times);
00251
00252 do
00253 {
00254 CuboidMeshConstructor<DIM> constructor;
00255
00256
00257 double printing_step = this->PdeTimeStep;
00258 #define COVERAGE_IGNORE
00259 while (printing_step < 1.0e-4)
00260 {
00261 printing_step *= 2.0;
00262 std::cout<<"Warning: PrintingTimeStep increased\n";
00263 }
00264 #undef COVERAGE_IGNORE
00265 HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(this->OdeTimeStep, this->PdeTimeStep, printing_step);
00266 #define COVERAGE_IGNORE
00267 if (SimulateFullActionPotential)
00268 {
00269 HeartConfig::Instance()->SetSimulationDuration(400.0);
00270 }
00271 else
00272 {
00273 HeartConfig::Instance()->SetSimulationDuration(8.0);
00274 }
00275 #undef COVERAGE_IGNORE
00276 HeartConfig::Instance()->SetOutputDirectory ("Convergence");
00277 HeartConfig::Instance()->SetOutputFilenamePrefix ("Results");
00278
00279
00280
00281
00282 TetrahedralMesh<DIM, DIM> mesh;
00283 TrianglesMeshReader<DIM, DIM> mesh_reader(constructor.Construct(this->MeshNum, mMeshWidth) );
00284 mesh.ConstructFromMeshReader(mesh_reader);
00285
00286 unsigned num_ele_across = (unsigned) pow(2, this->MeshNum+2);
00287
00288 AbstractCardiacCellFactory<DIM>* p_cell_factory=NULL;
00289
00290 switch (this->Stimulus)
00291 {
00292 case NEUMANN:
00293 {
00294 p_cell_factory = new ZeroStimulusCellFactory<CELL, DIM>();
00295 break;
00296 }
00297 case PLANE:
00298 {
00299 if (this->UseAbsoluteStimulus)
00300 {
00301 #define COVERAGE_IGNORE
00302 p_cell_factory = new GeneralPlaneStimulusCellFactory<CELL, DIM>(0, this->AbsoluteStimulus, true);
00303 #undef COVERAGE_IGNORE
00304 }
00305 else
00306 {
00307 p_cell_factory = new GeneralPlaneStimulusCellFactory<CELL, DIM>(num_ele_across, constructor.GetWidth());
00308 }
00309 break;
00310 }
00311 case REGION:
00312 {
00313 p_cell_factory = new QuarterStimulusCellFactory<CELL, DIM>(constructor.GetWidth());
00314 break;
00315 }
00316 }
00317
00318
00319 CARDIAC_PROBLEM cardiac_problem(p_cell_factory);
00321 cardiac_problem.SetMesh(&mesh);
00322
00323
00324 unsigned third_quadrant_node;
00325 unsigned first_quadrant_node;
00326 switch(DIM)
00327 {
00328 case 1:
00329 {
00330 first_quadrant_node = (unsigned) (0.25*constructor.NumElements);
00331 third_quadrant_node = (unsigned) (0.75*constructor.NumElements);
00332 break;
00333 }
00334 case 2:
00335 {
00336 unsigned n= (unsigned) pow (2, this->MeshNum+2);
00337 first_quadrant_node = (n+1)*(n/2)+ n/4 ;
00338 third_quadrant_node = (n+1)*(n/2)+3*n/4 ;
00339 break;
00340 }
00341 case 3:
00342 {
00343 const unsigned first_quadrant_nodes_3d[5]={61, 362, 2452, 17960, 137296};
00344 const unsigned third_quadrant_nodes_3d[5]={63, 366, 2460, 17976, 137328};
00345 assert(this->PdeTimeStep<5);
00346 first_quadrant_node = first_quadrant_nodes_3d[this->MeshNum];
00347 third_quadrant_node = third_quadrant_nodes_3d[this->MeshNum];
00348 break;
00349 }
00350
00351 default:
00352 NEVER_REACHED;
00353 }
00354
00355 double mesh_width=constructor.GetWidth();
00356
00357
00358 std::vector<unsigned> nodes_to_be_output;
00359 nodes_to_be_output.push_back(first_quadrant_node);
00360 nodes_to_be_output.push_back(third_quadrant_node);
00361 cardiac_problem.SetOutputNodes(nodes_to_be_output);
00362
00363
00364
00365
00366 SetConductivities(cardiac_problem);
00367
00368 cardiac_problem.Initialise();
00369
00370 #ifndef NDEBUG
00371 Node<DIM>* fqn = cardiac_problem.rGetMesh().GetNode(first_quadrant_node);
00372 Node<DIM>* tqn = cardiac_problem.rGetMesh().GetNode(third_quadrant_node);
00373 assert(fqn->rGetLocation()[0]==0.25*mesh_width);
00374 assert(fabs(tqn->rGetLocation()[0] - 0.75*mesh_width) < 1e-10);
00375 for (unsigned coord=1; coord<DIM; coord++)
00376 {
00377 assert(fqn->rGetLocation()[coord]==0.5*mesh_width);
00378 assert(tqn->rGetLocation()[coord]==0.5*mesh_width);
00379 }
00380 #endif
00381
00382 BoundaryConditionsContainer<DIM,DIM,PROBLEM_DIM> bcc;
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 bcc.AddNeumannBoundaryCondition(*iter, p_bc_stim);
00400 }
00401 iter++;
00402 }
00403
00404 cardiac_problem.SetBoundaryConditionsContainer(&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
00435 if (results_handler.IsMaster())
00436 {
00437 OutputFileHandler plot_file_handler("ConvergencePlots", false);
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 if (results_handler.IsMaster())
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 OutputFileHandler plot_file_handler("ConvergencePlots", false);
00454 std::stringstream plot_file_name_stream;
00455 plot_file_name_stream<< nameOfTest << "_First_quadrant_node_run_"<< file_num << ".csv";
00456 out_stream p_plot_file = plot_file_handler.OpenOutputFile(plot_file_name_stream.str());
00457 for (unsigned data_point = 0; data_point<time_series.size(); data_point++)
00458 {
00459 (*p_plot_file) << time_series_1qd[data_point] << "\t" << transmembrane_potential_1qd[data_point] << "\n";
00460 }
00461 p_plot_file->close();
00462 }
00463
00464
00465 PropagationPropertiesCalculator ppc(&results_reader);
00466
00467
00468 try
00469 {
00470 #define COVERAGE_IGNORE
00471 if (SimulateFullActionPotential)
00472 {
00473 Apd90FirstQn = ppc.CalculateActionPotentialDuration(90.0, first_quadrant_node);
00474 Apd90ThirdQn = ppc.CalculateActionPotentialDuration(90.0, third_quadrant_node);
00475 }
00476 #undef COVERAGE_IGNORE
00477 ConductionVelocity = ppc.CalculateConductionVelocity(first_quadrant_node,third_quadrant_node,0.5*mesh_width);
00478 }
00479 catch (Exception e)
00480 {
00481 #define COVERAGE_IGNORE
00482 std::cout<<"Warning - this run threw an exception in calculating propagation. Check convergence results\n";
00483 std::cout<<e.GetMessage() << std::endl;
00484 #undef COVERAGE_IGNORE
00485 }
00486 double cond_velocity_error = 1e10;
00487 double apd90_first_qn_error = 1e10;
00488 double apd90_third_qn_error = 1e10;
00489
00490 if (this->PopulatedResult)
00491 {
00492 if (prev_cond_velocity != 0.0)
00493 {
00494 cond_velocity_error = fabs(ConductionVelocity - prev_cond_velocity) / prev_cond_velocity;
00495 }
00496 #define COVERAGE_IGNORE
00497 if (prev_apd90_first_qn != 0.0)
00498 {
00499 apd90_first_qn_error = fabs(Apd90FirstQn - prev_apd90_first_qn) / prev_apd90_first_qn;
00500 }
00501 if (prev_apd90_third_qn != 0.0)
00502 {
00503 apd90_third_qn_error = fabs(Apd90ThirdQn - prev_apd90_third_qn) / prev_apd90_third_qn;
00504 }
00505 if (apd90_first_qn_error == 0.0)
00506 {
00507 apd90_first_qn_error = DBL_EPSILON;
00508 }
00509 if (apd90_third_qn_error == 0.0)
00510 {
00511 apd90_third_qn_error = DBL_EPSILON;
00512 }
00513 #undef COVERAGE_IGNORE
00514 if (cond_velocity_error == 0.0)
00515 {
00516 cond_velocity_error = DBL_EPSILON;
00517 }
00518 }
00519
00520 prev_cond_velocity = ConductionVelocity;
00521 prev_apd90_first_qn = Apd90FirstQn;
00522 prev_apd90_third_qn = Apd90ThirdQn;
00523
00524
00525 double max_abs_error = 0;
00526 double sum_sq_abs_error =0;
00527 double sum_sq_prev_voltage = 0;
00528 double sum_sq_abs_error_full =0;
00529 double sum_sq_prev_voltage_full = 0;
00530 if (this->PopulatedResult)
00531 {
00532
00533
00534 unsigned time_factor=(time_series.size()-1) / (prev_times.size()-1);
00535 assert (time_factor == 1 || time_factor == 2 || time_factor == 8);
00536
00537 for (unsigned data_point = 0; data_point<prev_times.size(); data_point++)
00538 {
00539 unsigned this_data_point=time_factor*data_point;
00540
00541 assert(time_series[this_data_point] == prev_times[data_point]);
00542 double abs_error = fabs(transmembrane_potential[this_data_point]-prev_voltage[data_point]);
00543 max_abs_error = (abs_error > max_abs_error) ? abs_error : max_abs_error;
00544
00545 sum_sq_abs_error_full += abs_error*abs_error;
00546 sum_sq_prev_voltage_full += prev_voltage[data_point] * prev_voltage[data_point];
00547 if (time_series[this_data_point] <= 8.0)
00548 {
00549
00550 sum_sq_abs_error += abs_error*abs_error;
00551 sum_sq_prev_voltage += prev_voltage[data_point] * prev_voltage[data_point];
00552 }
00553 }
00554
00555 }
00556 if (!this->PopulatedResult || !FixedResult)
00557 {
00558 prev_voltage = transmembrane_potential;
00559 prev_times = time_series;
00560 }
00561
00562 if (this->PopulatedResult)
00563 {
00564
00565 if (conv_info_handler.IsMaster())
00566 {
00567 (*p_conv_info_file) << std::setprecision(8)
00568 << Abscissa() << "\t"
00569 << sqrt(sum_sq_abs_error_full/sum_sq_prev_voltage_full) << "\t"
00570 << sqrt(sum_sq_abs_error/sum_sq_prev_voltage) << "\t"
00571 << max_abs_error << "\t"
00572 << Apd90FirstQn <<" "<< apd90_first_qn_error <<""<< "\t"
00573 << Apd90ThirdQn <<" "<< apd90_third_qn_error <<""<< "\t"
00574 << ConductionVelocity <<" "<< cond_velocity_error <<""<< std::endl;
00575 }
00576
00577 this->Converged = sum_sq_abs_error/sum_sq_prev_voltage<this->RelativeConvergenceCriterion;
00578 this->LastDifference=sum_sq_abs_error/sum_sq_prev_voltage;
00579 #define COVERAGE_IGNORE
00580 if (time_series.size() == 1u)
00581 {
00582 std::cout<<"Failed after successful convergence - give up this convergence test\n";
00583 break;
00584 }
00585 #undef COVERAGE_IGNORE
00586
00587 }
00588
00589 if (!this->PopulatedResult)
00590 {
00591 this->PopulatedResult=true;
00592
00593 }
00594 }
00595
00596
00597 if (!this->Converged)
00598 {
00599 UpdateConvergenceParameters();
00600 file_num++;
00601 }
00602 delete p_cell_factory;
00603 }
00604 while (!GiveUpConvergence() && !this->Converged);
00605
00606
00607 if (conv_info_handler.IsMaster())
00608 {
00609 p_conv_info_file->close();
00610
00611 std::cout << "Results: " << std::endl;
00612 EXPECT0(system, "cat " + conv_info_handler.GetOutputDirectoryFullPath() + nameOfTest + "_info.csv");
00613 }
00614
00615 }
00616
00617 void DisplayRun()
00618 {
00619 unsigned num_ele_across = (unsigned) pow(2, this->MeshNum+2);
00620 double scaling = mMeshWidth/(double) num_ele_across;
00621
00622 std::cout<<"================================================================================"<<std::endl;
00623 std::cout<<"Solving in "<<DIM<<"D\t";
00624 std::cout<<"Space step "<< scaling << " cm (mesh " << this->MeshNum << ")" << "\n";
00625 std::cout<<"PDE step "<<this->PdeTimeStep<<" ms"<<"\t";
00626 std::cout<<"ODE step "<<this->OdeTimeStep<<" ms"<<"\t";
00627 if (HeartConfig::Instance()->GetUseAbsoluteTolerance())
00628 {
00629 std::cout<<"KSP absolute "<<HeartConfig::Instance()->GetAbsoluteTolerance()<<"\t";
00630 }
00631 else
00632 {
00633 std::cout<<"KSP relative "<<HeartConfig::Instance()->GetRelativeTolerance()<<"\t";
00634 }
00635 switch (this->Stimulus)
00636 {
00637 case PLANE:
00638 std::cout<<"Stimulus = Plane\n";
00639 break;
00640
00641 case REGION:
00642 std::cout<<"Stimulus = Region\n";
00643 break;
00644
00645 case NEUMANN:
00646 std::cout<<"Stimulus = Neumann\n";
00647 break;
00648
00649 }
00650 EXPECT0(system, "date");
00653 if (this->UseAbsoluteStimulus)
00654 {
00655 #define COVERAGE_IGNORE
00656 std::cout<<"Using absolute stimulus of "<<this->AbsoluteStimulus<<std::endl;
00657 #undef COVERAGE_IGNORE
00658 }
00659 std::cout << std::flush;
00660
00661
00662
00663 }
00664
00665 public:
00666 virtual ~AbstractConvergenceTester() {}
00667
00669 virtual void SetInitialConvergenceParameters()=0;
00671 virtual void UpdateConvergenceParameters()=0;
00673 virtual bool GiveUpConvergence()=0;
00675 virtual double Abscissa()=0;
00682 virtual void PopulateStandardResult(std::vector<double> &result, std::vector<double> ×)
00683 {
00684 assert(this->PopulatedResult==false);
00685 assert(result.size()==0);
00686 assert(times.size()==0);
00687 }
00688
00692 bool IsConverged()
00693 {
00694 return Converged;
00695 }
00696
00700 void SetMeshWidth(double meshWidth)
00701 {
00702 mMeshWidth=meshWidth;
00703 }
00704 };
00705
00706 #endif