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;
}
Пример #3
0
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);
    }
}
Пример #5
0
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();

    }
Пример #9
0
    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)
         *
         */


    }
Пример #13
0
    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
    }
Пример #14
0
    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();
            }
        }

    }