void genunimp () { console_reset(); #ifdef PCHIST ppc(); #endif /* PCHIST */ suspend(); console_init(); return; }
/** * Run the same test at different levels of refinement until some convergence criterion is met. * @param nameOfTest The name of the convergence test (typically the name in the suite) for use in naming files. * \todo This is a scarily long method; could do with some parts extracted? */ void Converge(std::string nameOfTest) { // Create the meshes on which the test will be based const std::string mesh_dir = "ConvergenceMesh"; OutputFileHandler output_file_handler(mesh_dir); ReplicatableVector voltage_replicated; unsigned file_num=0; // Create a file for storing conduction velocity and AP data and write the header OutputFileHandler conv_info_handler("ConvergencePlots"+nameOfTest, false); out_stream p_conv_info_file; if (PetscTools::AmMaster()) { std::cout << "=========================== Beginning Test...==================================\n"; p_conv_info_file = conv_info_handler.OpenOutputFile(nameOfTest+"_info.csv"); (*p_conv_info_file) << "#Abcisa\t" << "l2-norm-full\t" << "l2-norm-onset\t" << "Max absolute err\t" << "APD90_1st_quad\t" << "APD90_3rd_quad\t" << "Conduction velocity (relative diffs)" << std::endl; } SetInitialConvergenceParameters(); double prev_apd90_first_qn=0.0; double prev_apd90_third_qn=0.0; double prev_cond_velocity=0.0; std::vector<double> prev_voltage; std::vector<double> prev_times; PopulateStandardResult(prev_voltage, prev_times); do { CuboidMeshConstructor<DIM> constructor; //If the printing time step is too fine, then simulations become I/O bound without much improvement in accuracy double printing_step = this->PdeTimeStep; #define COVERAGE_IGNORE while (printing_step < 1.0e-4) { printing_step *= 2.0; std::cout<<"Warning: PrintingTimeStep increased\n"; } #undef COVERAGE_IGNORE HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(this->OdeTimeStep, this->PdeTimeStep, printing_step); #define COVERAGE_IGNORE if (SimulateFullActionPotential) { HeartConfig::Instance()->SetSimulationDuration(400.0); } else { HeartConfig::Instance()->SetSimulationDuration(8.0); } #undef COVERAGE_IGNORE HeartConfig::Instance()->SetOutputDirectory("Convergence"+nameOfTest); HeartConfig::Instance()->SetOutputFilenamePrefix("Results"); DistributedTetrahedralMesh<DIM, DIM> mesh; constructor.Construct(mesh, this->MeshNum, mMeshWidth); unsigned num_ele_across = SmallPow(2u, this->MeshNum+2); // number of elements in each dimension AbstractCardiacCellFactory<DIM>* p_cell_factory=NULL; switch (this->Stimulus) { case NEUMANN: { p_cell_factory = new ZeroStimulusCellFactory<CELL, DIM>(); break; } case PLANE: { if (this->UseAbsoluteStimulus) { #define COVERAGE_IGNORE p_cell_factory = new GeneralPlaneStimulusCellFactory<CELL, DIM>(0, this->AbsoluteStimulus, true); #undef COVERAGE_IGNORE } else { p_cell_factory = new GeneralPlaneStimulusCellFactory<CELL, DIM>(num_ele_across, constructor.GetWidth(), false, this->AbsoluteStimulus); } break; } case QUARTER: { ///\todo consider reducing all stimuli to match this one. p_cell_factory = new RampedQuarterStimulusCellFactory<CELL, DIM>(constructor.GetWidth(), num_ele_across, this->AbsoluteStimulus/10.0); break; } default: NEVER_REACHED; } CARDIAC_PROBLEM cardiac_problem(p_cell_factory); cardiac_problem.SetMesh(&mesh); // Calculate positions of nodes 1/4 and 3/4 through the mesh unsigned third_quadrant_node; unsigned first_quadrant_node; switch(DIM) { case 1: { first_quadrant_node = (unsigned) (0.25*constructor.GetNumElements()); third_quadrant_node = (unsigned) (0.75*constructor.GetNumElements()); break; } case 2: { unsigned n= SmallPow (2u, this->MeshNum+2); first_quadrant_node = (n+1)*(n/2)+ n/4 ; third_quadrant_node = (n+1)*(n/2)+3*n/4 ; break; } case 3: { const unsigned first_quadrant_nodes_3d[5]={61, 362, 2452, 17960, 137296}; const unsigned third_quadrant_nodes_3d[5]={63, 366, 2460, 17976, 137328}; assert(this->PdeTimeStep<5); first_quadrant_node = first_quadrant_nodes_3d[this->MeshNum]; third_quadrant_node = third_quadrant_nodes_3d[this->MeshNum]; break; } default: NEVER_REACHED; } double mesh_width=constructor.GetWidth(); // We only need the output of these two nodes std::vector<unsigned> nodes_to_be_output; nodes_to_be_output.push_back(first_quadrant_node); nodes_to_be_output.push_back(third_quadrant_node); cardiac_problem.SetOutputNodes(nodes_to_be_output); // The results of the tests were originally obtained with the following conductivity // values. After implementing fibre orientation the defaults changed. Here we set // the former ones to be used. SetConductivities(cardiac_problem); cardiac_problem.Initialise(); boost::shared_ptr<BoundaryConditionsContainer<DIM,DIM,PROBLEM_DIM> > p_bcc(new BoundaryConditionsContainer<DIM,DIM,PROBLEM_DIM>); SimpleStimulus stim(NeumannStimulus, 0.5); if (Stimulus==NEUMANN) { StimulusBoundaryCondition<DIM>* p_bc_stim = new StimulusBoundaryCondition<DIM>(&stim); // get mesh AbstractTetrahedralMesh<DIM, DIM> &r_mesh = cardiac_problem.rGetMesh(); // loop over boundary elements typename AbstractTetrahedralMesh<DIM, DIM>::BoundaryElementIterator iter; iter = r_mesh.GetBoundaryElementIteratorBegin(); while (iter != r_mesh.GetBoundaryElementIteratorEnd()) { double x = ((*iter)->CalculateCentroid())[0]; ///\todo remove magic number? (#1884) if (x*x<=1e-10) { p_bcc->AddNeumannBoundaryCondition(*iter, p_bc_stim); } iter++; } // pass the bcc to the problem cardiac_problem.SetBoundaryConditionsContainer(p_bcc); } DisplayRun(); Timer::Reset(); //// use this to get some info printed out //cardiac_problem.SetWriteInfo(); try { cardiac_problem.Solve(); } catch (Exception e) { WARNING("This run threw an exception. Check convergence results\n"); std::cout << e.GetMessage() << std::endl; } if (PetscTools::AmMaster()) { std::cout << "Time to solve = " << Timer::GetElapsedTime() << " seconds\n"; } OutputFileHandler results_handler("Convergence"+nameOfTest, false); Hdf5DataReader results_reader = cardiac_problem.GetDataReader(); { std::vector<double> transmembrane_potential = results_reader.GetVariableOverTime("V", third_quadrant_node); std::vector<double> time_series = results_reader.GetUnlimitedDimensionValues(); OutputFileHandler plot_file_handler("ConvergencePlots"+nameOfTest, false); if (PetscTools::AmMaster()) { // Write out the time series for the node at third quadrant { std::stringstream plot_file_name_stream; plot_file_name_stream<< nameOfTest << "_Third_quadrant_node_run_"<< file_num << ".csv"; out_stream p_plot_file = plot_file_handler.OpenOutputFile(plot_file_name_stream.str()); for (unsigned data_point = 0; data_point<time_series.size(); data_point++) { (*p_plot_file) << time_series[data_point] << "\t" << transmembrane_potential[data_point] << "\n"; } p_plot_file->close(); } // Write time series for first quadrant node { std::vector<double> transmembrane_potential_1qd=results_reader.GetVariableOverTime("V", first_quadrant_node); std::vector<double> time_series_1qd = results_reader.GetUnlimitedDimensionValues(); std::stringstream plot_file_name_stream; plot_file_name_stream<< nameOfTest << "_First_quadrant_node_run_"<< file_num << ".csv"; out_stream p_plot_file = plot_file_handler.OpenOutputFile(plot_file_name_stream.str()); for (unsigned data_point = 0; data_point<time_series.size(); data_point++) { (*p_plot_file) << time_series_1qd[data_point] << "\t" << transmembrane_potential_1qd[data_point] << "\n"; } p_plot_file->close(); } } // calculate conduction velocity and APD90 error PropagationPropertiesCalculator ppc(&results_reader); try { #define COVERAGE_IGNORE if (SimulateFullActionPotential) { Apd90FirstQn = ppc.CalculateActionPotentialDuration(90.0, first_quadrant_node); Apd90ThirdQn = ppc.CalculateActionPotentialDuration(90.0, third_quadrant_node); } #undef COVERAGE_IGNORE ConductionVelocity = ppc.CalculateConductionVelocity(first_quadrant_node,third_quadrant_node,0.5*mesh_width); } catch (Exception e) { #define COVERAGE_IGNORE std::cout << "Warning - this run threw an exception in calculating propagation. Check convergence results\n"; std::cout << e.GetMessage() << std::endl; #undef COVERAGE_IGNORE } double cond_velocity_error = 1e10; double apd90_first_qn_error = 1e10; double apd90_third_qn_error = 1e10; if (this->PopulatedResult) { if (prev_cond_velocity != 0.0) { cond_velocity_error = fabs(ConductionVelocity - prev_cond_velocity) / prev_cond_velocity; } #define COVERAGE_IGNORE if (prev_apd90_first_qn != 0.0) { apd90_first_qn_error = fabs(Apd90FirstQn - prev_apd90_first_qn) / prev_apd90_first_qn; } if (prev_apd90_third_qn != 0.0) { apd90_third_qn_error = fabs(Apd90ThirdQn - prev_apd90_third_qn) / prev_apd90_third_qn; } if (apd90_first_qn_error == 0.0) { apd90_first_qn_error = DBL_EPSILON; //Avoid log zero on plot } if (apd90_third_qn_error == 0.0) { apd90_third_qn_error = DBL_EPSILON; //Avoid log zero on plot } #undef COVERAGE_IGNORE if (cond_velocity_error == 0.0) { cond_velocity_error = DBL_EPSILON; //Avoid log zero on plot } } prev_cond_velocity = ConductionVelocity; prev_apd90_first_qn = Apd90FirstQn; prev_apd90_third_qn = Apd90ThirdQn; // calculate l2norm double max_abs_error = 0; double sum_sq_abs_error =0; double sum_sq_prev_voltage = 0; double sum_sq_abs_error_full =0; double sum_sq_prev_voltage_full = 0; if (this->PopulatedResult) { //If the PDE step is varying then we'll have twice as much data now as we use to have unsigned time_factor=(time_series.size()-1) / (prev_times.size()-1); assert (time_factor == 1 || time_factor == 2 || time_factor == 8); //Iterate over the shorter time series data for (unsigned data_point = 0; data_point<prev_times.size(); data_point++) { unsigned this_data_point=time_factor*data_point; assert(time_series[this_data_point] == prev_times[data_point]); double abs_error = fabs(transmembrane_potential[this_data_point]-prev_voltage[data_point]); max_abs_error = (abs_error > max_abs_error) ? abs_error : max_abs_error; //Only do resolve the upstroke... sum_sq_abs_error_full += abs_error*abs_error; sum_sq_prev_voltage_full += prev_voltage[data_point] * prev_voltage[data_point]; if (time_series[this_data_point] <= 8.0) { //In most regular cases we always do this, since the simulation stops at ms sum_sq_abs_error += abs_error*abs_error; sum_sq_prev_voltage += prev_voltage[data_point] * prev_voltage[data_point]; } } } if (!this->PopulatedResult || !FixedResult) { prev_voltage = transmembrane_potential; prev_times = time_series; } if (this->PopulatedResult) { double l2_norm_upstroke = sqrt(sum_sq_abs_error/sum_sq_prev_voltage); double l2_norm_full = sqrt(sum_sq_abs_error_full/sum_sq_prev_voltage_full); if (PetscTools::AmMaster()) { (*p_conv_info_file) << std::setprecision(8) << Abscissa() << "\t" << l2_norm_full << "\t" << l2_norm_upstroke << "\t" << max_abs_error << "\t" << Apd90FirstQn <<" "<< apd90_first_qn_error <<""<< "\t" << Apd90ThirdQn <<" "<< apd90_third_qn_error <<""<< "\t" << ConductionVelocity <<" "<< cond_velocity_error <<""<< std::endl; } // convergence criterion this->Converged = l2_norm_full < this->RelativeConvergenceCriterion; this->LastDifference = l2_norm_full; #define COVERAGE_IGNORE assert (time_series.size() != 1u); if (time_series.back() == 0.0) { std::cout << "Failed after successful convergence - give up this convergence test\n"; break; } #undef COVERAGE_IGNORE } if ( time_series.back() != 0.0) { // Simulation ran to completion this->PopulatedResult=true; } } // Get ready for the next test by halving the time step if (!this->Converged) { UpdateConvergenceParameters(); file_num++; } delete p_cell_factory; } while (!GiveUpConvergence() && !this->Converged); if (PetscTools::AmMaster()) { p_conv_info_file->close(); std::cout << "Results: " << std::endl; FileFinder info_finder = conv_info_handler.FindFile(nameOfTest + "_info.csv"); std::ifstream info_file(info_finder.GetAbsolutePath().c_str()); if (info_file) { std::cout << info_file.rdbuf(); info_file.close(); } } }