void TysonNovak2001OdeSystem::EvaluateYDerivatives(double time, const std::vector<double>& rY, std::vector<double>& rDY) { double x1 = rY[0]; double x2 = rY[1]; double x3 = rY[2]; double x4 = rY[3]; double x5 = rY[4]; double x6 = rY[5]; double dx1 = 0.0; double dx2 = 0.0; double dx3 = 0.0; double dx4 = 0.0; double dx5 = 0.0; double dx6 = 0.0; double temp1 = 0.0; double temp2 = 0.0; double temp3 = 0.0; /** * 1. [CycB] * 2. [Cdh1] * 3. [Cdc20T] * 4. [Cdc20A] * 5. [IEP] * 6. m - mass of the cell */ dx1 = mK1-(mK2d+mK2dd*x2)*x1; // The commented line below models the start transition, no cycling, without Cdc20A // temp1 = ((mK3d)*(1.0-x2))/(mJ3+1.0-x2); temp1 = ((mK3d+mK3dd*x4)*(1.0-x2))/(mJ3+1.0-x2); temp2 = (mK4*x6*x1*x2)/(mJ4+x2); dx2 = temp1-temp2; temp1 = mK5dd*(SmallPow(x1*x6/mJ5,mN)/(1+SmallPow(x1*x6/mJ5,mN))); temp2 = mK6*x3; dx3 = mK5d + temp1 - temp2; temp1 = (mK7*x5*(x3-x4))/(mJ7+x3-x4); temp2 = (mK8*mMad*x4)/(mJ8+x4); temp3 = mK6*x4; dx4 = temp1 - temp2 - temp3; dx5 = mK9*x6*x1*(1.0-x5) - mK10*x5; dx6 = mMu*x6*(1.0-x6/mMstar); // Multiply by 60 beacuase the Tyson and Novak 2001 paper has time in minutes, not hours rDY[0] = dx1*60.0; rDY[1] = dx2*60.0; rDY[2] = dx3*60.0; rDY[3] = dx4*60.0; rDY[4] = dx5*60.0; rDY[5] = dx6*60.0; }
void NhsContractionModel::EvaluateYDerivatives(double time, const std::vector<double> &rY, std::vector<double> &rDY) { //// if making changes here, see also NhsModelWithBackwardSolver::CalculateCaTropAndZDerivatives() const double& calcium_troponin = rY[0]; const double& z = rY[1]; const double& Q1 = rY[2]; const double& Q2 = rY[3]; const double& Q3 = rY[4]; // check the state vars are in the expected range #define COVERAGE_IGNORE if(calcium_troponin < 0) { EXCEPTION("CalciumTrop concentration went negative"); } if(z<0) { EXCEPTION("z went negative"); } if(z>1) { EXCEPTION("z became greater than 1"); } #undef COVERAGE_IGNORE double Q = Q1 + Q2 + Q3; double T0 = CalculateT0(z); double Ta; if(Q>0) { Ta = T0*(1+(2+mA)*Q)/(1+Q); } else { Ta = T0*(1+mA*Q)/(1-Q); } rDY[0] = mKon * mCalciumI * ( mCalciumTroponinMax - calcium_troponin) - mKrefoff * (1-Ta/(mGamma*mTref)) * calcium_troponin; double ca_trop_to_ca_trop50_ratio_to_n = SmallPow(calcium_troponin/mCalciumTrop50, mN); rDY[1] = mAlpha0 * ca_trop_to_ca_trop50_ratio_to_n * (1-z) - mAlphaR1 * z - mAlphaR2 * SmallPow(z,mNr) / (SmallPow(z,mNr) + SmallPow(mKZ,mNr)); rDY[2] = mA1 * mDLambdaDt - mAlpha1 * Q1; rDY[3] = mA2 * mDLambdaDt - mAlpha2 * Q2; rDY[4] = mA3 * mDLambdaDt - mAlpha3 * Q3; }
void NodeBasedCellPopulation<DIM>::NonBlockingSendCellsToNeighbourProcesses() { #if BOOST_VERSION < 103700 EXCEPTION("Parallel cell-based Chaste requires Boost >= 1.37"); #else // BOOST_VERSION >= 103700 if (!PetscTools::AmTopMost()) { boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_right(&mCellsToSendRight, null_deleter()); int tag = SmallPow(2u, 1+ PetscTools::GetMyRank() ) * SmallPow (3u, 1 + PetscTools::GetMyRank() + 1); mRightCommunicator.ISendObject(p_cells_right, PetscTools::GetMyRank() + 1, tag); } if (!PetscTools::AmMaster()) { int tag = SmallPow (2u, 1 + PetscTools::GetMyRank() ) * SmallPow (3u, 1 + PetscTools::GetMyRank() - 1); boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_left(&mCellsToSendLeft, null_deleter()); mLeftCommunicator.ISendObject(p_cells_left, PetscTools::GetMyRank() - 1, tag); } // Now post receives to start receiving data before returning. if (!PetscTools::AmTopMost()) { int tag = SmallPow (3u, 1 + PetscTools::GetMyRank() ) * SmallPow (2u, 1+ PetscTools::GetMyRank() + 1); mRightCommunicator.IRecvObject(PetscTools::GetMyRank() + 1, tag); } if (!PetscTools::AmMaster()) { int tag = SmallPow (3u, 1 + PetscTools::GetMyRank() ) * SmallPow (2u, 1+ PetscTools::GetMyRank() - 1); mLeftCommunicator.IRecvObject(PetscTools::GetMyRank() - 1, tag); } #endif }
void NodeBasedCellPopulation<DIM>::NonBlockingSendCellsToNeighbourProcesses() { if (!PetscTools::AmTopMost()) { boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_right(&mCellsToSendRight, null_deleter()); int tag = SmallPow(2u, 1+ PetscTools::GetMyRank() ) * SmallPow (3u, 1 + PetscTools::GetMyRank() + 1); mRightCommunicator.ISendObject(p_cells_right, PetscTools::GetMyRank() + 1, tag); } if (!PetscTools::AmMaster()) { int tag = SmallPow (2u, 1 + PetscTools::GetMyRank() ) * SmallPow (3u, 1 + PetscTools::GetMyRank() - 1); boost::shared_ptr<std::vector<std::pair<CellPtr, Node<DIM>* > > > p_cells_left(&mCellsToSendLeft, null_deleter()); mLeftCommunicator.ISendObject(p_cells_left, PetscTools::GetMyRank() - 1, tag); } // Now post receives to start receiving data before returning. if (!PetscTools::AmTopMost()) { int tag = SmallPow (3u, 1 + PetscTools::GetMyRank() ) * SmallPow (2u, 1+ PetscTools::GetMyRank() + 1); mRightCommunicator.IRecvObject(PetscTools::GetMyRank() + 1, tag); } if (!PetscTools::AmMaster()) { int tag = SmallPow (3u, 1 + PetscTools::GetMyRank() ) * SmallPow (2u, 1+ PetscTools::GetMyRank() - 1); mLeftCommunicator.IRecvObject(PetscTools::GetMyRank() - 1, tag); } }
void VertexCryptBoundaryForce<DIM>::AddForceContribution(AbstractCellPopulation<DIM>& rCellPopulation) { // Helper variable that is a static cast of the cell population VertexBasedCellPopulation<DIM>* p_cell_population = static_cast<VertexBasedCellPopulation<DIM>*>(&rCellPopulation); // Throw an exception message if not using a VertexBasedCellPopulation if (dynamic_cast<VertexBasedCellPopulation<DIM>*>(&rCellPopulation) == nullptr) { EXCEPTION("VertexCryptBoundaryForce is to be used with VertexBasedCellPopulations only"); } // Iterate over nodes for (typename AbstractMesh<DIM,DIM>::NodeIterator node_iter = p_cell_population->rGetMesh().GetNodeIteratorBegin(); node_iter != p_cell_population->rGetMesh().GetNodeIteratorEnd(); ++node_iter) { double y = node_iter->rGetLocation()[1]; // y-coordinate of node // If the node lies below the line y=0, then add the boundary force contribution to the node forces if (y < 0.0) { c_vector<double, DIM> boundary_force = zero_vector<double>(DIM); boundary_force[1] = mForceStrength*SmallPow(y, 2); node_iter->AddAppliedForceContribution(boundary_force); } } }
double NhsContractionModel::CalculateT0(double z) { double calcium_ratio_to_n = SmallPow(mCalciumTrop50/mCalciumTroponinMax, mN); double z_max = mAlpha0 - mK2*calcium_ratio_to_n; z_max /= mAlpha0 + (mAlphaR1 + mK1)*calcium_ratio_to_n; return z * mTref * (1+mBeta0*(mLambda-1)) / z_max; }
void RunTest(unsigned numStepsInEachDimension) { MeshEventHandler::Reset(); unsigned nodes = SmallPow(numStepsInEachDimension+1, 3); if (PetscTools::AmMaster()) { std::cout<<"Number steps per dimension = " << std::setw(12) << numStepsInEachDimension<<std::endl; std::cout<<"Number nodes = " << std::setw(12) << nodes<<std::endl; } std::string file_name = "cuboid"; std::stringstream directory_stream; directory_stream << "TestMeshWritersWeekly/steps_" << std::setw(12) << std::setfill('0')<< numStepsInEachDimension; std::string directory_name = directory_stream.str(); DistributedTetrahedralMesh<3,3> cuboid_mesh; cuboid_mesh.ConstructCuboid(numStepsInEachDimension, numStepsInEachDimension, numStepsInEachDimension); TS_ASSERT_EQUALS(cuboid_mesh.GetNumNodes(), nodes); MeshEventHandler::BeginEvent(MeshEventHandler::TRIANGLES); { TrianglesMeshWriter<3,3> mesh_writer(directory_name, file_name, false); mesh_writer.WriteFilesUsingMesh(cuboid_mesh); } MeshEventHandler::EndEvent(MeshEventHandler::TRIANGLES); MeshEventHandler::BeginEvent(MeshEventHandler::BINTRI); { TrianglesMeshWriter<3,3> bin_mesh_writer(directory_name, file_name+"_bin", false); bin_mesh_writer.SetWriteFilesAsBinary(); bin_mesh_writer.WriteFilesUsingMesh(cuboid_mesh); } MeshEventHandler::EndEvent(MeshEventHandler::BINTRI); #ifdef CHASTE_VTK MeshEventHandler::BeginEvent(MeshEventHandler::VTK); { VtkMeshWriter<3,3> vtk_writer(directory_name, file_name, false); vtk_writer.WriteFilesUsingMesh(cuboid_mesh); } MeshEventHandler::EndEvent(MeshEventHandler::VTK); MeshEventHandler::BeginEvent(MeshEventHandler::PVTK); { VtkMeshWriter<3,3> parallel_vtk_writer(directory_name, file_name+"par",false); parallel_vtk_writer.SetParallelFiles(cuboid_mesh); std::vector<double> dummy_data(cuboid_mesh.GetNumLocalNodes(), PetscTools::GetMyRank()); parallel_vtk_writer.AddPointData("Process", dummy_data); parallel_vtk_writer.WriteFilesUsingMesh(cuboid_mesh); } MeshEventHandler::EndEvent(MeshEventHandler::PVTK); #endif //CHASTE_VTK MeshEventHandler::Headings(); MeshEventHandler::Report(); }
void DisplayRun() { if (!PetscTools::AmMaster()) { return; //Only master displays this } unsigned num_ele_across = SmallPow(2u, this->MeshNum+2);// number of elements in each dimension double scaling = mMeshWidth/(double) num_ele_across; std::cout<<"================================================================================"<<std::endl; std::cout<<"Solving in " << DIM << "D\t"; std::cout<<"Space step " << scaling << " cm (mesh " << this->MeshNum << ")" << "\n"; std::cout<<"PDE step " << this->PdeTimeStep << " ms" << "\t"; std::cout<<"ODE step " << this->OdeTimeStep << " ms" << "\t"; if (HeartConfig::Instance()->GetUseAbsoluteTolerance()) { std::cout<<"KSP absolute "<<HeartConfig::Instance()->GetAbsoluteTolerance()<<"\t"; } else { std::cout<<"KSP relative "<<HeartConfig::Instance()->GetRelativeTolerance()<<"\t"; } switch (this->Stimulus) { case PLANE: std::cout<<"Stimulus = Plane\n"; break; case QUARTER: std::cout<<"Stimulus = Ramped first quarter\n"; break; case NEUMANN: std::cout<<"Stimulus = Neumann\n"; break; } // Keep track of what Nightly things are doing std::time_t rawtime; std::time( &rawtime ); std::cout << std::ctime(&rawtime); ///\todo The UseAbsoluteStimulus is temporary, while we are sorting out ///3D stimulus. It is to be removed later (along with StimulusConvergenceTester) if (this->UseAbsoluteStimulus) { #define COVERAGE_IGNORE std::cout<<"Using absolute stimulus of "<<this->AbsoluteStimulus<<std::endl; #undef COVERAGE_IGNORE } std::cout << std::flush; //HeartEventHandler::Headings(); //HeartEventHandler::Report(); }
void TestColemanDynamicVentilationSingleAirway() throw(Exception) { #ifdef LUNG_USE_UMFPACK FileFinder mesh_finder("lung/test/data/single_branch", RelativeTo::ChasteSourceRoot); double compliance = 0.1/98.0665/1e3; //in m^3 / pa. Converted from 0.1 L/cmH2O per lung. SimpleAcinarUnitFactory factory(compliance, 2400.0); double viscosity = 1.92e-5; //Pa s double terminal_airway_radius = 0.0005; //m double terminal_airway_length = 0.005; //m double terminal_airway_resistance = 8*viscosity*terminal_airway_length/(M_PI*SmallPow(terminal_airway_radius, 4)); double ode_volume = 0.0; DynamicVentilationProblem problem(&factory, mesh_finder.GetAbsolutePath(), 0u); problem.rGetMatrixVentilationProblem().SetMeshInMilliMetres(); problem.rGetMatrixVentilationProblem().SetOutflowPressure(0.0); problem.SetTimeStep(0.01); TimeStepper time_stepper(0.0, 1.0, 0.01); while (!time_stepper.IsTimeAtEnd()) { //Solve corresponding backward Euler problem for testing double pleural_pressure = factory.GetPleuralPressureForNode(time_stepper.GetNextTime(), NULL); double dt = time_stepper.GetNextTimeStep(); ode_volume = (ode_volume - dt*pleural_pressure/terminal_airway_resistance)/(1 + dt/(terminal_airway_resistance*compliance)); //Solve using DynamicVentilationProblem problem.SetEndTime(time_stepper.GetNextTime()); problem.Solve(); std::map<unsigned, AbstractAcinarUnit*>& r_acinar_map = problem.rGetAcinarUnitMap(); TS_ASSERT_DELTA(ode_volume, r_acinar_map[5]->GetVolume(), 1e-6); time_stepper.AdvanceOneTimeStep(); } #else std::cout << "Warning: This test needs UMFPACK to execute correctly." << std::endl; #endif }
NhsContractionModel::NhsContractionModel() : AbstractOdeBasedContractionModel(5) // five state variables { mpSystemInfo = OdeSystemInformation<NhsContractionModel>::Instance(); ResetToInitialConditions(); mLambda = 1.0; mDLambdaDt = 0.0; mCalciumI = 0.0; // Initialise mCalciumTrop50!! CalculateCalciumTrop50(); double zp_to_n_plus_K_to_n = SmallPow(mZp,mNr) + SmallPow(mKZ,mNr); mK1 = mAlphaR2 * SmallPow(mZp,mNr-1) * mNr * SmallPow(mKZ,mNr); mK1 /= zp_to_n_plus_K_to_n * zp_to_n_plus_K_to_n; mK2 = mAlphaR2 * SmallPow(mZp,mNr)/zp_to_n_plus_K_to_n; mK2 *= 1 - mNr*SmallPow(mKZ,mNr)/zp_to_n_plus_K_to_n; }
void TysonNovak2001OdeSystem::AnalyticJacobian(const std::vector<double>& rSolutionGuess, double** jacobian, double time, double timeStep) { timeStep *= 60.0; // to scale Jacobian so in hours not minutes double x1 = rSolutionGuess[0]; double x2 = rSolutionGuess[1]; double x3 = rSolutionGuess[2]; double x4 = rSolutionGuess[3]; double x5 = rSolutionGuess[4]; double x6 = rSolutionGuess[5]; // f1 double df1_dx1 = -mK2d - mK2dd*x2; double df1_dx2 = -mK2dd*x1; jacobian[0][0] = 1-timeStep*df1_dx1; jacobian[0][1] = -timeStep*df1_dx2; // f2 double df2_dx1 = -mK4*x6*x2/(mJ4+x2); double df2_dx2 = -mJ3*(mK3d + mK3dd*x4)/(SmallPow((mJ3 + 1 - x2),2)) -mJ4*mK4*x6*x1/(SmallPow((mJ4+x2),2)); double df2_dx4 = mK3dd*(1-x2)/(mJ3+1-x2); double df2_dx6 = -mK4*x1*x2/(mJ4+x2); jacobian[1][0] = -timeStep*df2_dx1; jacobian[1][1] = 1-timeStep*df2_dx2; jacobian[1][3] = -timeStep*df2_dx4; jacobian[1][5] = -timeStep*df2_dx6; //f3 double z = x1*x6/mJ5; double df3_dx1 = (mK5dd*x6/mJ5)*mN*SmallPow(z,mN-1)/(SmallPow((1-SmallPow(z,mN)),2)); double df3_dx3 = -mK6; double df3_dx6 = (mK5dd*x1/mJ5)*mN*SmallPow(z,mN-1)/(SmallPow((1-SmallPow(z,mN)),2)); jacobian[2][0] = -timeStep*df3_dx1; jacobian[2][2] = 1-timeStep*df3_dx3; jacobian[2][5] = -timeStep*df3_dx6; // f4 double df4_dx3 = mJ7*mK7*x5/(SmallPow(mJ7+x3-x4,2)); double df4_dx4 = -mJ7*mK7*x5/(SmallPow(mJ7+x3-x4,2)) - mK6 - mJ8*mK8*mMad/(SmallPow(mJ8+x4,2)); double df4_dx5 = mK7*(x3-x4)/(mJ7+x3-x4); jacobian[3][2] = -timeStep*df4_dx3; jacobian[3][3] = 1-timeStep*df4_dx4; jacobian[3][4] = -timeStep*df4_dx5; // f5 double df5_dx1 = mK9*x6*(1-x5); double df5_dx5 = -mK10 - mK9*x6*x1; double df5_dx6 = mK9*x1*(1-x5); jacobian[4][0] = -timeStep*df5_dx1; jacobian[4][4] = 1-timeStep*df5_dx5; jacobian[4][5] = -timeStep*df5_dx6; // f6 double df6_dx6 = mMu - 2*mMu*x6/mMstar; jacobian[5][5] = 1-timeStep*df6_dx6; }
void TestRunAllTests() { for (unsigned pow = 0; pow<6; pow++) //unsigned pow = 5; { if (PetscTools::AmMaster()) { std::cout<<"Power = " << std::setw(12) << pow<<std::endl; } unsigned steps = SmallPow(2u, pow); RunTest(steps); } /* See #2351 * Rough times (sequential, 2-way, 3-way, 4-way GccOpt) * pow nodes time time2 time3 time4 4 4K 1s 1s 8s 16s 5 36K 5s 22s >5m >5 6 280K 45s >5m 7 2M (uses more than 4Gb RAM) */ /* Timing of pow=5 test for IntelProduction: * Power = 5 Number steps per dimension = 32 Number nodes = 35937 Construct Tri write Bin write VTK write PVTK write Total 1.794 ( 38%) 0.303 ( 6%) 0.152 ( 3%) 1.196 ( 25%) 1.330 ( 28%) 4.775 (100%) (seconds) Proc Construct Tri write Bin write VTK write PVTK write Total 0: 0.959 ( 5%) 5.778 ( 29%) 5.604 ( 28%) 6.886 ( 35%) 0.727 ( 4%) 19.955 (100%) (seconds) 1: 0.929 ( 5%) 5.809 ( 29%) 5.604 ( 28%) 6.886 ( 35%) 0.559 ( 3%) 19.956 (100%) (seconds) avg: 0.944 ( 5%) 5.794 ( 29%) 5.604 ( 28%) 6.886 ( 35%) 0.643 ( 3%) 19.955 (100%) (seconds) max: 0.959 ( 5%) 5.809 ( 29%) 5.604 ( 28%) 6.886 ( 35%) 0.727 ( 4%) 19.956 (100%) (seconds) Proc Construct Tri write Bin write VTK write PVTK write Total 0: 0.508 ( 0%) 252.454 ( 31%) 231.030 ( 28%) 328.700 ( 40%) 0.314 ( 0%) 813.054 (100%) (seconds) 1: 0.507 ( 0%) 252.455 ( 31%) 231.030 ( 28%) 328.700 ( 40%) 0.362 ( 0%) 813.054 (100%) (seconds) 2: 0.442 ( 0%) 252.520 ( 31%) 231.030 ( 28%) 328.700 ( 40%) 0.315 ( 0%) 813.054 (100%) (seconds) 3: 0.399 ( 0%) 252.564 ( 31%) 231.030 ( 28%) 328.700 ( 40%) 0.281 ( 0%) 813.054 (100%) (seconds) avg: 0.464 ( 0%) 252.498 ( 31%) 231.030 ( 28%) 328.700 ( 40%) 0.318 ( 0%) 813.054 (100%) (seconds) max: 0.508 ( 0%) 252.564 ( 31%) 231.030 ( 28%) 328.700 ( 40%) 0.362 ( 0%) 813.054 (100%) (seconds) Proc Construct Tri write Bin write VTK write PVTK write Total 0: 0.255 ( 0%) 727.416 ( 25%) 1109.748 ( 38%) 1094.729 ( 37%) 0.190 ( 0%) 2932.420 (100%) (seconds) 1: 0.254 ( 0%) 727.416 ( 25%) 1109.748 ( 38%) 1094.728 ( 37%) 0.273 ( 0%) 2932.419 (100%) (seconds) 2: 0.254 ( 0%) 727.417 ( 25%) 1109.748 ( 38%) 1094.728 ( 37%) 0.198 ( 0%) 2932.420 (100%) (seconds) 3: 0.254 ( 0%) 727.417 ( 25%) 1109.748 ( 38%) 1094.728 ( 37%) 0.236 ( 0%) 2932.419 (100%) (seconds) 4: 0.255 ( 0%) 727.416 ( 25%) 1109.748 ( 38%) 1094.728 ( 37%) 0.189 ( 0%) 2932.420 (100%) (seconds) 5: 0.258 ( 0%) 727.413 ( 25%) 1109.748 ( 38%) 1094.728 ( 37%) 0.185 ( 0%) 2932.419 (100%) (seconds) 6: 0.253 ( 0%) 727.418 ( 25%) 1109.748 ( 38%) 1094.728 ( 37%) 0.185 ( 0%) 2932.420 (100%) (seconds) 7: 0.209 ( 0%) 727.463 ( 25%) 1109.748 ( 38%) 1094.728 ( 37%) 0.149 ( 0%) 2932.420 (100%) (seconds) avg: 0.249 ( 0%) 727.422 ( 25%) 1109.748 ( 38%) 1094.728 ( 37%) 0.201 ( 0%) 2932.420 (100%) (seconds) max: 0.258 ( 0%) 727.463 ( 25%) 1109.748 ( 38%) 1094.729 ( 37%) 0.273 ( 0%) 2932.420 (100%) (seconds) * */ // Changes at r18242 /* Using finer timings and barrier synchronisation * Number nodes = 35937 Tri write node write ele write face write spare Total 0.299 (100%) 0.086 ( 29%) 0.202 ( 67%) 0.011 ( 4%) 0.000 ( 0%) 0.299 (100%) (seconds) Proc Tri write node write ele write face write spare Total 0: 1.421 (100%) 0.095 ( 7%) 1.254 ( 88%) 0.071 ( 5%) 0.000 ( 0%) 1.421 (100%) (seconds) 1: 1.480 (100%) 0.095 ( 6%) 1.254 ( 85%) 0.071 ( 5%) 0.000 ( 0%) 1.480 (100%) (seconds) avg: 1.450 (100%) 0.095 ( 7%) 1.254 ( 86%) 0.071 ( 5%) 0.000 ( 0%) 1.450 (100%) (seconds) max: 1.480 (100%) 0.095 ( 6%) 1.254 ( 85%) 0.071 ( 5%) 0.000 ( 0%) 1.480 (100%) (seconds) Proc Tri write node write ele write face write spare Total 0: 242.766 (100%) 1.768 ( 1%) 240.746 ( 99%) 0.252 ( 0%) 0.000 ( 0%) 242.766 (100%) (seconds) 1: 242.772 (100%) 1.768 ( 1%) 240.746 ( 99%) 0.252 ( 0%) 0.000 ( 0%) 242.772 (100%) (seconds) 2: 242.774 (100%) 1.768 ( 1%) 240.746 ( 99%) 0.252 ( 0%) 0.000 ( 0%) 242.774 (100%) (seconds) 3: 242.815 (100%) 1.768 ( 1%) 240.746 ( 99%) 0.252 ( 0%) 0.000 ( 0%) 242.815 (100%) (seconds) avg: 242.782 (100%) 1.768 ( 1%) 240.746 ( 99%) 0.252 ( 0%) 0.000 ( 0%) 242.782 (100%) (seconds) max: 242.815 (100%) 1.768 ( 1%) 240.746 ( 99%) 0.252 ( 0%) 0.000 ( 0%) 242.815 (100%) (seconds) Proc Tri write node write ele write face write spare Total 0: 903.342 (100%) 2.740 ( 0%) 900.387 (100%) 0.214 ( 0%) 0.000 ( 0%) 903.342 (100%) (seconds) 1: 903.348 (100%) 2.740 ( 0%) 900.387 (100%) 0.214 ( 0%) 0.000 ( 0%) 903.348 (100%) (seconds) 2: 903.347 (100%) 2.740 ( 0%) 900.387 (100%) 0.214 ( 0%) 0.000 ( 0%) 903.347 (100%) (seconds) 3: 903.349 (100%) 2.740 ( 0%) 900.387 (100%) 0.214 ( 0%) 0.000 ( 0%) 903.349 (100%) (seconds) 4: 903.346 (100%) 2.740 ( 0%) 900.387 (100%) 0.214 ( 0%) 0.000 ( 0%) 903.346 (100%) (seconds) 5: 903.350 (100%) 2.740 ( 0%) 900.387 (100%) 0.214 ( 0%) 0.000 ( 0%) 903.350 (100%) (seconds) 6: 903.344 (100%) 2.740 ( 0%) 900.387 (100%) 0.214 ( 0%) 0.000 ( 0%) 903.344 (100%) (seconds) 7: 903.394 (100%) 2.740 ( 0%) 900.387 (100%) 0.214 ( 0%) 0.000 ( 0%) 903.394 (100%) (seconds) avg: 903.352 (100%) 2.740 ( 0%) 900.387 (100%) 0.214 ( 0%) 0.000 ( 0%) 903.352 (100%) (seconds) max: 903.394 (100%) 2.740 ( 0%) 900.387 (100%) 0.214 ( 0%) 0.000 ( 0%) 903.394 (100%) (seconds) * */ // Changes at r18411 (or r18412 !) /* Using synchronised (blocking) sends * Power = 5 Number steps per dimension = 32 Number nodes = 35937 Tri write BinTri write VTK write PVTK write node write ele write face write ncl write comm1 comm2 Total 0.309 ( 11%) 0.163 ( 6%) 1.203 ( 42%) 1.197 ( 42%) 0.090 ( 3%) 0.311 ( 11%) 0.018 ( 1%) 0.052 ( 2%) 0.000 ( 0%) 0.000 ( 0%) 2.872 (100%) (seconds) Proc Tri write BinTri write VTK write PVTK write node write ele write face write ncl write comm1 comm2 Total 0: 1.525 ( 26%) 1.332 ( 22%) 2.404 ( 41%) 0.673 ( 11%) 0.114 ( 2%) 2.534 ( 43%) 0.158 ( 3%) 0.050 ( 1%) 0.255 ( 4%) 0.131 ( 2%) 5.932 (100%) (seconds) 1: 1.560 ( 26%) 1.332 ( 22%) 2.403 ( 40%) 0.608 ( 10%) 0.129 ( 2%) 4.736 ( 79%) 0.231 ( 4%) 0.050 ( 1%) 4.716 ( 79%) 0.160 ( 3%) 5.968 (100%) (seconds) avg: 1.542 ( 26%) 1.332 ( 22%) 2.403 ( 40%) 0.640 ( 11%) 0.122 ( 2%) 3.635 ( 61%) 0.194 ( 3%) 0.050 ( 1%) 2.486 ( 42%) 0.146 ( 2%) 5.950 (100%) (seconds) max: 1.560 ( 26%) 1.332 ( 22%) 2.404 ( 40%) 0.673 ( 11%) 0.129 ( 2%) 4.736 ( 79%) 0.231 ( 4%) 0.050 ( 1%) 4.716 ( 79%) 0.160 ( 3%) 5.968 (100%) (seconds) Proc Tri write BinTri write VTK write PVTK write node write ele write face write ncl write comm1 comm2 Total 0: 2.215 ( 28%) 2.029 ( 26%) 3.290 ( 41%) 0.411 ( 5%) 0.122 ( 2%) 3.850 ( 48%) 0.212 ( 3%) 0.060 ( 1%) 0.457 ( 6%) 0.308 ( 4%) 7.945 (100%) (seconds) 1: 2.217 ( 28%) 2.029 ( 26%) 3.289 ( 41%) 0.385 ( 5%) 0.062 ( 1%) 2.630 ( 33%) 4.327 ( 54%) 0.060 ( 1%) 6.823 ( 86%) 0.085 ( 1%) 7.947 (100%) (seconds) 2: 2.219 ( 28%) 2.029 ( 26%) 3.289 ( 41%) 0.358 ( 5%) 0.111 ( 1%) 4.917 ( 62%) 2.074 ( 26%) 0.060 ( 1%) 6.756 ( 85%) 0.187 ( 2%) 7.949 (100%) (seconds) 3: 2.259 ( 28%) 2.029 ( 25%) 3.289 ( 41%) 0.301 ( 4%) 0.146 ( 2%) 6.787 ( 85%) 0.312 ( 4%) 0.060 ( 1%) 6.978 ( 87%) 0.076 ( 1%) 7.989 (100%) (seconds) avg: 2.228 ( 28%) 2.029 ( 25%) 3.290 ( 41%) 0.364 ( 5%) 0.111 ( 1%) 4.546 ( 57%) 1.731 ( 22%) 0.060 ( 1%) 5.253 ( 66%) 0.164 ( 2%) 7.957 (100%) (seconds) max: 2.259 ( 28%) 2.029 ( 25%) 3.290 ( 41%) 0.411 ( 5%) 0.146 ( 2%) 6.787 ( 85%) 4.327 ( 54%) 0.060 ( 1%) 6.978 ( 87%) 0.308 ( 4%) 7.989 (100%) (seconds) Proc Tri write BinTri write VTK write PVTK write node write ele write face write ncl write comm1 comm2 Total 0: 2.648 ( 28%) 2.496 ( 27%) 3.898 ( 42%) 0.210 ( 2%) 0.144 ( 2%) 4.682 ( 50%) 0.251 ( 3%) 0.065 ( 1%) 0.586 ( 6%) 0.475 ( 5%) 9.312 (100%) (seconds) 1: 2.649 ( 28%) 2.496 ( 27%) 3.897 ( 42%) 0.271 ( 3%) 0.035 ( 0%) 1.490 ( 16%) 6.891 ( 74%) 0.065 ( 1%) 8.308 ( 89%) 0.046 ( 0%) 9.313 (100%) (seconds) 2: 2.652 ( 28%) 2.496 ( 27%) 3.897 ( 42%) 0.246 ( 3%) 0.061 ( 1%) 2.683 ( 29%) 5.716 ( 61%) 0.065 ( 1%) 8.272 ( 89%) 0.100 ( 1%) 9.316 (100%) (seconds) 3: 2.652 ( 28%) 2.496 ( 27%) 3.897 ( 42%) 0.203 ( 2%) 0.087 ( 1%) 3.885 ( 42%) 4.531 ( 49%) 0.065 ( 1%) 8.291 ( 89%) 0.097 ( 1%) 9.316 (100%) (seconds) 4: 2.647 ( 28%) 2.496 ( 27%) 3.897 ( 42%) 0.190 ( 2%) 0.105 ( 1%) 5.005 ( 54%) 3.431 ( 37%) 0.065 ( 1%) 8.364 ( 90%) 0.046 ( 0%) 9.311 (100%) (seconds) 5: 2.646 ( 28%) 2.496 ( 27%) 3.897 ( 42%) 0.192 ( 2%) 0.124 ( 1%) 6.124 ( 66%) 2.334 ( 25%) 0.065 ( 1%) 8.384 ( 90%) 0.046 ( 0%) 9.310 (100%) (seconds) 6: 2.651 ( 28%) 2.496 ( 27%) 3.897 ( 42%) 0.189 ( 2%) 0.150 ( 2%) 7.311 ( 78%) 1.164 ( 12%) 0.065 ( 1%) 8.352 ( 90%) 0.095 ( 1%) 9.315 (100%) (seconds) 7: 2.694 ( 29%) 2.496 ( 27%) 3.897 ( 42%) 0.155 ( 2%) 0.176 ( 2%) 8.193 ( 88%) 0.373 ( 4%) 0.065 ( 1%) 8.463 ( 90%) 0.080 ( 1%) 9.358 (100%) (seconds) avg: 2.655 ( 28%) 2.496 ( 27%) 3.897 ( 42%) 0.207 ( 2%) 0.110 ( 1%) 4.921 ( 53%) 3.087 ( 33%) 0.065 ( 1%) 7.378 ( 79%) 0.123 ( 1%) 9.319 (100%) (seconds) max: 2.694 ( 29%) 2.496 ( 27%) 3.898 ( 42%) 0.271 ( 3%) 0.176 ( 2%) 8.193 ( 88%) 6.891 ( 74%) 0.065 ( 1%) 8.463 ( 90%) 0.475 ( 5%) 9.358 (100%) (seconds) * */ }
void TestColemanDynamicVentilationOtisBifurcations() throw(Exception) { #ifdef LUNG_USE_UMFPACK FileFinder mesh_finder("lung/test/data/otis_bifurcation", RelativeTo::ChasteSourceRoot); //The otis bifurcation mesh defines two branches of unequal radii leading to two acini //Analytical results for this system can be found in Otis et al. Journal of Applied Physiology 1956 double total_compliance = 0.1/98.0665/1e3; //in m^3 / pa. Converted from 0.1 L/cmH2O per lung to four acinar compartments double viscosity = 1.92e-5; //Pa s double radius_zero = 0.002; //m double radius_one = 0.0002; //m double radius_two = 0.001; //m double length = 0.001; //m double R0 = 8*length*viscosity/(M_PI*SmallPow(radius_zero, 4)); double R1 = 8*length*viscosity/(M_PI*SmallPow(radius_one, 4)); double R2 = 8*length*viscosity/(M_PI*SmallPow(radius_two, 4)); double C1 = total_compliance/2.0; double C2 = total_compliance/2.0; double T1 = C1*R1; double T2 = C2*R2; double frequency = 2; //Hz double omega = 2*M_PI*frequency; double effective_compliance = (SmallPow(omega, 2)*SmallPow(T2*C1 + T1*C2, 2) + SmallPow(C1 + C2, 2)) / (SmallPow(omega, 2)*(SmallPow(T1,2)*C2 + SmallPow(T2,2)*C2) + C1 + C2); double effective_resistance = R0 + (SmallPow(omega,2)*T1*T2*(T2*C1 + T1*C2) + (T1*C1 + T2*C2)) / (SmallPow(omega,2)*SmallPow(T2*C1 + T1*C2,2) + SmallPow(C1 + C2, 2)); double theta = std::atan(1/(omega*effective_resistance*effective_compliance)); double delta_p = 500; SimpleAcinarUnitFactory factory(C1, delta_p/2.0, frequency); DynamicVentilationProblem problem(&factory, mesh_finder.GetAbsolutePath(), 0u); problem.rGetMatrixVentilationProblem().SetMeshInMilliMetres(); problem.rGetMatrixVentilationProblem().SetRadiusOnEdge(); problem.rGetMatrixVentilationProblem().SetOutflowPressure(0.0); double expected_tidal_volume = effective_compliance*delta_p*std::sin(theta); //Setup a simulation iterating between the flow solver and the acinar balloon. problem.SetTimeStep(0.0005); problem.SetEndTime(16.0); problem.Solve(); //Solve to 16s to allow the problem to equilibriate double min_total_volume = 0.0; double max_total_volume = 0.0; //Now solve the last four seconds recording the tidal volume. TimeStepper time_stepper(16.0, 20.0, 0.0005); while (!time_stepper.IsTimeAtEnd()) { problem.SetEndTime(time_stepper.GetNextTime()); problem.Solve(); std::map<unsigned, AbstractAcinarUnit*>& r_acinar_map = problem.rGetAcinarUnitMap(); double total_volume = r_acinar_map[2]->GetVolume() + r_acinar_map[3]->GetVolume(); if(min_total_volume > total_volume) { min_total_volume = total_volume; } if(max_total_volume < total_volume) { max_total_volume = total_volume; } time_stepper.AdvanceOneTimeStep(); } TS_ASSERT_DELTA(expected_tidal_volume, max_total_volume - min_total_volume, 1e-7); #else std::cout << "Warning: This test needs UMFPACK to execute correctly." << std::endl; #endif }
void TestColemanDynamicVentilationThreeBifurcations() throw(Exception) { FileFinder mesh_finder("lung/test/data/three_bifurcations", RelativeTo::ChasteSourceRoot); //The three bifurcation mesh defines a fully symmetric three bifurcation airway tree. //The composite ventilation problem is then equivalent to a trumpet problem connected //to an acinus with compliance equal to the total compliance of all the acini. double total_compliance = 0.1/98.0665/1e3; //in m^3 / pa. Converted from 0.1 L/cmH2O per lung to four acinar compartments double acinar_compliance = total_compliance/4.0; SimpleAcinarUnitFactory factory(acinar_compliance, 2400.0); TS_ASSERT_THROWS_CONTAINS(factory.GetMesh(), "The mesh object has not been set in the acinar unit factory"); double viscosity = 1.92e-5; //Pa s double terminal_airway_radius = 0.00005; //m double resistance_per_unit_length = 8*viscosity/(M_PI*SmallPow(terminal_airway_radius, 4)); //All airways in the mesh have radius 0.05 mm. The first branch is 3mm long, the others are 5mm. double total_airway_resistance = (0.003 + 0.005/2 + 0.005/4)*resistance_per_unit_length; double ode_volume = 0.0; //Setup a simulation iterating between the flow solver and the acinar balloon. DynamicVentilationProblem problem(&factory, mesh_finder.GetAbsolutePath(), 0u); problem.rGetMatrixVentilationProblem().SetOutflowPressure(0.0); problem.rGetMatrixVentilationProblem().SetMeshInMilliMetres(); problem.SetTimeStep(0.01); factory.GetNumberOfAcini(); TimeStepper time_stepper(0.0, 1.0, 0.01); while (!time_stepper.IsTimeAtEnd()) { //Solve corresponding backward Euler problem for testing double pleural_pressure = factory.GetPleuralPressureForNode(time_stepper.GetNextTime(), NULL); double dt = time_stepper.GetNextTimeStep(); ode_volume = (ode_volume - dt*pleural_pressure/total_airway_resistance)/(1 + dt/(total_airway_resistance*total_compliance)); //Solve using DynamicVentilationProblem problem.SetEndTime(time_stepper.GetNextTime()); problem.Solve(); std::map<unsigned, AbstractAcinarUnit*>& r_acinar_map = problem.rGetAcinarUnitMap(); TS_ASSERT_DELTA(ode_volume, r_acinar_map[5]->GetVolume(), 1e-6); time_stepper.AdvanceOneTimeStep(); } //Solve for longer and write output to VTK problem.SetSamplingTimeStepMultiple(10u); problem.SetOutputDirectory("TestDynamicVentilation"); problem.SetOutputFilenamePrefix("three_bifurcations"); problem.SetWriteVtkOutput(); problem.SetEndTime(1.5); problem.Solve(); #ifdef CHASTE_VTK std::string filepath = OutputFileHandler::GetChasteTestOutputDirectory() + "TestDynamicVentilation/"; std::string basename = filepath + "three_bifurcations"; FileFinder vtu_file(basename + ".vtu", RelativeTo::Absolute); TS_ASSERT(vtu_file.Exists()); #endif }
CylindricalHoneycombMeshGenerator::CylindricalHoneycombMeshGenerator(unsigned numNodesAlongWidth, unsigned numNodesAlongLength, unsigned ghosts, double scaleFactor) { mpMesh = NULL; mDomainWidth = numNodesAlongWidth*scaleFactor; mNumCellWidth = numNodesAlongWidth; //*1 because cells are considered to be size one mNumCellLength = numNodesAlongLength; mMeshFilename = "mesh"; mGhostNodeIndices.clear(); // The code below won't work in parallel assert(PetscTools::IsSequential()); // An older version of the constructor might allow the wrong argument through to the scale factor assert(scaleFactor > 0.0); // Get a unique mesh filename std::stringstream pid; pid << getpid(); OutputFileHandler output_file_handler("2D_temporary_honeycomb_mesh_"+ pid.str()); unsigned num_nodes_along_width = mNumCellWidth; unsigned num_nodes_along_length = mNumCellLength; double horizontal_spacing = mDomainWidth / (double)num_nodes_along_width; double vertical_spacing = (sqrt(3.0)/2)*horizontal_spacing; // This line is needed to define ghost nodes later mDomainDepth = (double)(num_nodes_along_length) * vertical_spacing; // Take account of ghost nodes num_nodes_along_length += 2*ghosts; unsigned num_nodes = num_nodes_along_width*num_nodes_along_length; unsigned num_elem_along_width = num_nodes_along_width-1; unsigned num_elem_along_length = num_nodes_along_length-1; unsigned num_elem = 2*num_elem_along_width*num_elem_along_length; unsigned num_edges = 3*num_elem_along_width*num_elem_along_length + num_elem_along_width + num_elem_along_length; double x0 = 0; double y0 = -vertical_spacing*ghosts; mBottom = -vertical_spacing*ghosts; mTop = mBottom + vertical_spacing*(num_nodes_along_length-1); // Write node file out_stream p_node_file = output_file_handler.OpenOutputFile(mMeshFilename+".node"); (*p_node_file) << std::scientific; //(*p_node_file) << std::setprecision(20); (*p_node_file) << num_nodes << "\t2\t0\t1" << std::endl; unsigned node = 0; for (unsigned i=0; i<num_nodes_along_length; i++) { for (unsigned j=0; j<num_nodes_along_width; j++) { if (i<ghosts || i>=(ghosts+mNumCellLength)) { mGhostNodeIndices.insert(node); } unsigned boundary = 0; if ((i==0) || (i==num_nodes_along_length-1)) { boundary = 1; } double x = x0 + horizontal_spacing*((double)j + 0.25*(1.0+ SmallPow(-1.0,i+1))); double y = y0 + vertical_spacing*(double)i; // Avoid floating point errors which upset OffLatticeSimulation if ( (y<0.0) && (y>-1e-12) ) { // Difficult to cover - just corrects floating point errors that have occurred from time to time! #define COVERAGE_IGNORE y = 0.0; #undef COVERAGE_IGNORE } (*p_node_file) << node++ << "\t" << x << "\t" << y << "\t" << boundary << std::endl; } } p_node_file->close(); // Write element file and edge file out_stream p_elem_file = output_file_handler.OpenOutputFile(mMeshFilename+".ele"); (*p_elem_file) << std::scientific; out_stream p_edge_file = output_file_handler.OpenOutputFile(mMeshFilename+".edge"); (*p_node_file) << std::scientific; (*p_elem_file) << num_elem << "\t3\t0" << std::endl; (*p_edge_file) << num_edges << "\t1" << std::endl; unsigned elem = 0; unsigned edge = 0; for (unsigned i=0; i<num_elem_along_length; i++) { for (unsigned j=0; j < num_elem_along_width; j++) { unsigned node0 = i*num_nodes_along_width + j; unsigned node1 = i*num_nodes_along_width + j+1; unsigned node2 = (i+1)*num_nodes_along_width + j; if (i%2 != 0) { node2 = node2 + 1; } (*p_elem_file) << elem++ << "\t" << node0 << "\t" << node1 << "\t" << node2 << std::endl; unsigned horizontal_edge_is_boundary_edge = 0; unsigned vertical_edge_is_boundary_edge = 0; if (i==0) { horizontal_edge_is_boundary_edge = 1; } (*p_edge_file) << edge++ << "\t" << node0 << "\t" << node1 << "\t" << horizontal_edge_is_boundary_edge << std::endl; (*p_edge_file) << edge++ << "\t" << node1 << "\t" << node2 << "\t" << 0 << std::endl; (*p_edge_file) << edge++ << "\t" << node2 << "\t" << node0 << "\t" << vertical_edge_is_boundary_edge << std::endl; node0 = i*num_nodes_along_width + j + 1; if (i%2 != 0) { node0 = node0 - 1; } node1 = (i+1)*num_nodes_along_width + j+1; node2 = (i+1)*num_nodes_along_width + j; (*p_elem_file) << elem++ << "\t" << node0 << "\t" << node1 << "\t" << node2 << std::endl; } } for (unsigned i=0; i<num_elem_along_length; i++) { unsigned node0, node1; if (i%2==0) { node0 = (i+1)*num_nodes_along_width - 1; node1 = (i+2)*num_nodes_along_width - 1; } else { node0 = (i+1)*num_nodes_along_width; node1 = (i)*num_nodes_along_width; } (*p_edge_file) << edge++ << "\t" << node0 << "\t" << node1 << "\t" << 1 << std::endl; } for (unsigned j=0; j<num_elem_along_width; j++) { unsigned node0 = num_nodes_along_width*(num_nodes_along_length-1) + j; unsigned node1 = num_nodes_along_width*(num_nodes_along_length-1) + j+1; (*p_edge_file) << edge++ << "\t" << node1 << "\t" << node0 << "\t" << 1 << std::endl; } p_elem_file->close(); p_edge_file->close(); // Having written the mesh to file, now construct it using TrianglesMeshReader. // Nested scope so the reader closes files before we delete them below. { TrianglesMeshReader<2,2> mesh_reader(output_file_handler.GetOutputDirectoryFullPath() + mMeshFilename); mpMesh = new Cylindrical2dMesh(mDomainWidth); mpMesh->ConstructFromMeshReader(mesh_reader); } // Make the mesh cylindrical (we use Triangle library mode inside this ReMesh call) mpMesh->ReMesh(); // Delete the temporary files output_file_handler.FindFile("").Remove(); // The original files have been deleted, it is better if the mesh object forgets about them mpMesh->SetMeshHasChangedSinceLoading(); }
/** * 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(); } } }