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); } } }