AbstractConvergenceTester.hpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2009
00004 
00005 University of Oxford means the Chancellor, Masters and Scholars of the
00006 University of Oxford, having an administrative office at Wellington
00007 Square, Oxford OX1 2JD, UK.
00008 
00009 This file is part of Chaste.
00010 
00011 Chaste is free software: you can redistribute it and/or modify it
00012 under the terms of the GNU Lesser General Public License as published
00013 by the Free Software Foundation, either version 2.1 of the License, or
00014 (at your option) any later version.
00015 
00016 Chaste is distributed in the hope that it will be useful, but WITHOUT
00017 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00019 License for more details. The offer of Chaste under the terms of the
00020 License is subject to the License being interpreted in accordance with
00021 English Law and subject to any action against the University of Oxford
00022 being under the jurisdiction of the English Courts.
00023 
00024 You should have received a copy of the GNU Lesser General Public License
00025 along with Chaste. If not, see <http://www.gnu.org/licenses/>.
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     // define a new stimulus
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     //bool UseNeumannStimulus;
00122     bool Converged;
00123     //bool StimulateRegion;
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         // Create the meshes on which the test will be based
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         // Create a file for storing conduction velocity and AP data and write the header
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             //Otherwise we can't take the timestep down to machine precision without generating thousands of output files            
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             //Don't use parallel mesh for now
00232             //HeartConfig::Instance()->SetMeshFileName( constructor.Construct(this->MeshNum, mMeshWidth) );
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); // number of elements in each dimension
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 //                cardiac_problem.SetLinearSolverAbsoluteTolerance(this->mKspTolerance);
00279             }
00280             else
00281             {
00282                 HeartConfig::Instance()->SetUseRelativeTolerance(this->mKspTolerance);
00283 
00284 //                cardiac_problem.SetLinearSolverRelativeTolerance(this->mKspTolerance);
00285             }
00286 
00287             // Calculate positions of nodes 1/4 and 3/4 through the mesh
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             // We only need the output of these two nodes
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             // The results of the tests were originally obtained with the following conductivity
00329             // values. After implementing fibre orientation the defaults changed. Here we set
00330             // the former ones to be used.
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                 // get mesh
00355                 AbstractMesh<DIM, DIM> &r_mesh = cardiac_problem.rGetMesh();
00356                 // loop over boundary elements
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                 // pass the bcc to the problem
00369                 cardiac_problem.SetBoundaryConditionsContainer(&bcc);
00370             }
00371 
00372               DisplayRun();
00373             double time_before=MPI_Wtime();
00375             //cardiac_problem.SetWriteInfo();
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                 // Write out the time series for the node at third quadrant
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                 // Write time series for first quadrant node
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                 // calculate conduction velocity and APD90 error
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                     //std::cout << "APD90\n"
00455                     //          << "Current: " << apd90_first_qn << "\t" << apd90_third_qn << "\n"
00456                     //          << "Previous: " << prev_apd90_first_qn << "\t" << prev_apd90_third_qn << "\n";
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                 // calculate l2norm
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                     //std::cout << log10(Abscissa()) << "\t" << log10(sum_sq_abs_error/sum_sq_prev_voltage) <<"\t#Logs for Gnuplot\n";
00494                     //Use "set logscale x; set logscale y" to get loglog plots in Gnuplot
00495                     //std::cout << Abscissa() << "\t" << sum_sq_abs_error/sum_sq_prev_voltage <<"\t#Gnuplot raw data\n";
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                     // convergence criterion
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             // Get ready for the next test by halving the time step
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);// number of elements in each dimension
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");//To keep track of what Nightly things are doing
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 /*ABSTRACTCONVERGENCETESTER_HPP_*/

Generated on Wed Mar 18 12:51:50 2009 for Chaste by  doxygen 1.5.5