Esempio n. 1
0
void CheckMonoLr91Vars(MonodomainProblem<ELEMENT_DIM, SPACE_DIM>& problem)
{

    DistributedVector voltage = problem.rGetMesh().GetDistributedVectorFactory()->CreateDistributedVector(problem.GetSolution());
    for (DistributedVector::Iterator index = voltage.Begin();
         index != voltage.End();
         ++index)
    {
        // assuming LR model has Ena = 54.4 and Ek = -77
        double Ena   =  54.4;
        double Ek    = -77.0;

        TS_ASSERT_LESS_THAN_EQUALS( voltage[index] , Ena +  30);
        TS_ASSERT_LESS_THAN_EQUALS(-voltage[index] + (Ek-30), 0);

        std::vector<double> ode_vars = problem.GetMonodomainTissue()->GetCardiacCell(index.Global)->GetStdVecStateVariables();
        for (int j=0; j<8; j++)
        {
            // if not voltage or calcium ion conc, test whether between 0 and 1
            if ((j!=0) && (j!=7))
            {
                TS_ASSERT_LESS_THAN_EQUALS(  ode_vars[j], 1.0);
                TS_ASSERT_LESS_THAN_EQUALS( -ode_vars[j], 0.0);
            }
        }
    }
}
Vec AbstractCardiacProblem<ELEMENT_DIM,SPACE_DIM,PROBLEM_DIM>::CreateInitialCondition()
{
    DistributedVectorFactory* p_factory = mpMesh->GetDistributedVectorFactory();
    Vec initial_condition = p_factory->CreateVec(PROBLEM_DIM);
    DistributedVector ic = p_factory->CreateDistributedVector(initial_condition);
    std::vector<DistributedVector::Stripe> stripe;
    stripe.reserve(PROBLEM_DIM);

    for (unsigned i=0; i<PROBLEM_DIM; i++)
    {
        stripe.push_back(DistributedVector::Stripe(ic, i));
    }

    for (DistributedVector::Iterator index = ic.Begin();
         index != ic.End();
         ++index)
    {
        stripe[0][index] = mpCardiacTissue->GetCardiacCell(index.Global)->GetVoltage();
        if (PROBLEM_DIM==2)
        {
            stripe[1][index] = 0;
        }
    }

    ic.Restore();

    return initial_condition;
}
    // Solve a Purkinje problem and check that the solution for the myocardium nodes is exactly the same as
    // the solution of an equivalent monodomain-only problem, that the purkinje voltage for
    // purkinje nodes doesn't change (no purkinje stimulus is given), and is 0 for non-purkinje nodes.
    void TestMonodomainPurkinjeSolver()
    {
        /* The assembly requires 1/time-step,
         * here we are providing enough information without starting a whole simulation */
        PdeSimulationTime::SetTime(0.0);
        PdeSimulationTime::SetPdeTimeStepAndNextTime(0.01, 0.01);

        HeartConfig::Instance()->SetUseAbsoluteTolerance(1e-12);
        HeartConfig::Instance()->SetPurkinjeSurfaceAreaToVolumeRatio(HeartConfig::Instance()->GetSurfaceAreaToVolumeRatio());

        std::string mesh_base("mesh/test/data/mixed_dimension_meshes/2D_0_to_1mm_200_elements");
        TrianglesMeshReader<2,2> reader(mesh_base);
        MixedDimensionMesh<2,2> mesh(DistributedTetrahedralMeshPartitionType::DUMB);
        mesh.ConstructFromMeshReader(reader);

        //The mixed dimension mesh specifies fibre radii, we override these for the purposes of the
        //test
        for (MixedDimensionMesh<2, 2>::CableElementIterator iter = mesh.GetCableElementIteratorBegin();
             iter != mesh.GetCableElementIteratorEnd();
             ++iter)
        {
            (*iter)->SetAttribute(0.56419); //1/sqrt(pi), so that the fibre cross section area is 1.0
        }


        std::string mesh_base2("mesh/test/data/2D_0_to_1mm_200_elements");
        TrianglesMeshReader<2,2> reader2(mesh_base2);
        TetrahedralMesh<2,2> mesh_just_monodomain;
        mesh_just_monodomain.ConstructFromMeshReader(reader2);

        PurkinjeCellFactory cell_factory;
        cell_factory.SetMesh(&mesh);

        NonPurkinjeCellFactory cell_factory_for_just_monodomain;
        cell_factory_for_just_monodomain.SetMesh(&mesh_just_monodomain);

        MonodomainTissue<2> tissue( &cell_factory );
        MonodomainTissue<2> tissue_for_just_monodomain( &cell_factory_for_just_monodomain );

        // Create an empty BCC - zero Neumann BCs will be applied everywhere
        BoundaryConditionsContainer<2,2,2> bcc;
        BoundaryConditionsContainer<2,2,1> bcc_for_just_monodomain;

        MonodomainPurkinjeSolver<2,2> solver(&mesh, &tissue, &bcc);

        MonodomainSolver<2,2> solver_just_monodomain(&mesh_just_monodomain, &tissue_for_just_monodomain, &bcc_for_just_monodomain);


        //Create an initial condition
        DistributedVectorFactory* p_vector_factory = mesh.GetDistributedVectorFactory();
        Vec init_cond = p_vector_factory->CreateVec(2);
        Vec init_cond_just_monodomain = p_vector_factory->CreateVec(1);

        // get the voltage stripes
        DistributedVector ic = mesh.GetDistributedVectorFactory()->CreateDistributedVector(init_cond);
        DistributedVector ic2 = mesh.GetDistributedVectorFactory()->CreateDistributedVector(init_cond_just_monodomain);
        DistributedVector::Stripe volume_stripe = DistributedVector::Stripe(ic, 0);
        DistributedVector::Stripe cable_stripe = DistributedVector::Stripe(ic, 1);

        for (DistributedVector::Iterator index = ic.Begin();
             index != ic.End();
             ++index)
        {
            volume_stripe[index] = tissue.GetCardiacCell(index.Global)->GetVoltage();
            cable_stripe[index] = tissue.GetPurkinjeCell(index.Global)->GetVoltage();//doesn't matter if this is fake
            // make it zero in the cable stripe for the nodes that are not in purkinje ..
        }
        ic.Restore();

        for (DistributedVector::Iterator index = ic2.Begin();
             index != ic2.End();
             ++index)
        {
            ic2[index] = tissue.GetCardiacCell(index.Global)->GetVoltage();
        }
        ic2.Restore();


        double t_end = 1;
        solver.SetTimes(0, t_end);
        solver.SetTimeStep(0.01);
        solver.SetInitialCondition(init_cond);

        solver.SetOutputDirectoryAndPrefix("MonodomainPurkinje","results");
        solver.SetOutputToTxt(true);
        solver.SetPrintingTimestepMultiple(10);

        Vec solution = solver.Solve();

        // the following assumes Luo-Rudy!!
        Vec init_cond_for_just_monodomain = PetscTools::CreateAndSetVec(mesh.GetNumNodes(), -83.853);

        solver_just_monodomain.SetTimes(0, t_end);
        solver_just_monodomain.SetTimeStep(0.01);
        solver_just_monodomain.SetInitialCondition(init_cond_for_just_monodomain);

        Vec solution_just_monodomain = solver_just_monodomain.Solve();


        // Test that certain blocks of the matrix and rhs vector in the monodomain-purkinje solve
        // match the matrix and vector for a normal monodomain solve
        Vec& r_purk_rhs = solver.GetLinearSystem()->rGetRhsVector();
        Vec& r_mono_rhs = solver_just_monodomain.GetLinearSystem()->rGetRhsVector();

        Mat& r_purk_mat = solver.GetLinearSystem()->rGetLhsMatrix();
        Mat& r_mono_mat = solver_just_monodomain.GetLinearSystem()->rGetLhsMatrix();

        TS_ASSERT_EQUALS(PetscVecTools::GetSize(r_purk_rhs), 2*PetscVecTools::GetSize(r_mono_rhs));
        assert(PetscVecTools::GetSize(r_mono_rhs)==mesh.GetNumNodes());

        int lo, hi;
        VecGetOwnershipRange(r_mono_rhs, &lo, &hi);

        // We don't explicitly test the values of the rhs, as this is also tested by comparing the solutions.
        // As the system matrices are different, the results won't be identical (but will be close) for the same linear solver tolerance.
        // for(int i=lo; i<hi; i++)
        // {
        //     TS_ASSERT_DELTA(PetscVecTools::GetElement(r_purk_rhs, 2*i), PetscVecTools::GetElement(r_mono_rhs, i), 1e-8);
        // }

        for(int i=lo; i<hi; i++)
        {
            for(unsigned j=0; j<mesh.GetNumNodes(); j++)
            {
                // 'top-left' block
                TS_ASSERT_DELTA(PetscMatTools::GetElement(r_purk_mat, 2*i,2*j), PetscMatTools::GetElement(r_mono_mat, i,j), 1e-8);
                // 'off-diagonal' blocks
                TS_ASSERT_DELTA(PetscMatTools::GetElement(r_purk_mat, 2*i,2*j+1), 0.0, 1e-8);
                TS_ASSERT_DELTA(PetscMatTools::GetElement(r_purk_mat, 2*i+1,2*j), 0.0, 1e-8);
            }
        }


        ReplicatableVector soln_repl(solution);
        ReplicatableVector soln_mono_repl(solution_just_monodomain);

        for(unsigned i=0; i<mesh.GetNumNodes(); i++)
        {
            if(55<=i && i<=65) // purkinje nodes for this mesh
            {
                //The Purkinje domain is a 1D line embedded within the tissue.
                //It is stimulated in the same way as the tissue domain, therefore
                //the propagation velocity should be the same (within numerical error).
                TS_ASSERT_DELTA(soln_repl[2*i+1], soln_repl[2*i], 1e-1);
            }
            else
            {
                TS_ASSERT_DELTA(soln_repl[2*i+1], 0.0, 1e-4)
            }

            TS_ASSERT_DELTA(soln_repl[2*i], soln_mono_repl[i], 1e-5);
        }

        HeartConfig::Instance()->SetUseStateVariableInterpolation(true);
        TS_ASSERT_THROWS_THIS(MonodomainPurkinjeSolver2d bad_solver(&mesh, &tissue, &bcc),"State-variable interpolation is not yet supported with Purkinje");
        HeartConfig::Instance()->SetUseStateVariableInterpolation(false);


        PetscTools::Destroy(solution);
        PetscTools::Destroy(init_cond);
        PetscTools::Destroy(init_cond_for_just_monodomain);
        PetscTools::Destroy(solution_just_monodomain);
        PetscTools::Destroy(init_cond_just_monodomain);
    }
    // Solve on a 2D 1mm by 1mm mesh (space step = 0.1mm), stimulating the left
    // edge.
    void TestMonodomainFitzHughNagumoWithEdgeStimulus( void ) throw (Exception)
    {
        HeartConfig::Instance()->SetIntracellularConductivities(Create_c_vector(0.01, 0.01));
        HeartConfig::Instance()->SetSimulationDuration(1.2); //ms
        HeartConfig::Instance()->SetMeshFileName("mesh/test/data/2D_0_to_1mm_400_elements");
        HeartConfig::Instance()->SetOutputDirectory("FhnWithEdgeStimulus");
        HeartConfig::Instance()->SetOutputFilenamePrefix("MonodomainFhn_2dWithEdgeStimulus");

        FhnEdgeStimulusCellFactory cell_factory;

        // using the criss-cross mesh so wave propagates properly
        MonodomainProblem<2> monodomain_problem( &cell_factory );

        monodomain_problem.Initialise();

        HeartConfig::Instance()->SetSurfaceAreaToVolumeRatio(1.0);
        HeartConfig::Instance()->SetCapacitance(1.0);


        monodomain_problem.Solve();


        /*
        * Test the top right node against the right one in the 1D case,
        * comparing voltage, and then test all the nodes on the right hand
        * side of the square against the top right one, comparing voltage.
        */
        bool need_initialisation = true;
        double probe_voltage=0.0;

        DistributedVector voltage = monodomain_problem.GetSolutionDistributedVector();
        need_initialisation = true;

        // Test the RHS of the mesh
        for (DistributedVector::Iterator node_index = voltage.Begin();
             node_index != voltage.End();
             ++node_index)
        {
            if (monodomain_problem.rGetMesh().GetNode(node_index.Global)->GetPoint()[0] == 0.1)
            {
                // x = 0 is where the stimulus has been applied
                // x = 0.1cm is the other end of the mesh and where we want to
                //       to test the value of the nodes

                if (need_initialisation)
                {
                    probe_voltage = voltage[node_index];
                    need_initialisation = false;
                }
                else
                {
                    // Tests the final voltages for all the RHS edge nodes
                    // are close to each other.
                    // This works as we are using the 'criss-cross' mesh,
                    // the voltages would vary more with a mesh with all the
                    // triangles aligned in the same direction.

                    TS_ASSERT_DELTA(voltage[node_index], probe_voltage, 2e-4);
                }

                TS_ASSERT_DELTA(voltage[node_index], 0.139426, 2e-3);
             }
        }
    }