void Test2dHardcodedResult() throw(Exception) { PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 2> cell_factory(-1000*1000); // run to 125 ms - about where the width is at its minimum (see figures // in "A numerical method for cardiac mechano–electric simulations", Annals of Biomed. Imaging HeartConfig::Instance()->SetSimulationDuration(125.0); CardiacElectroMechProbRegularGeom<2> problem(INCOMPRESSIBLE, 1.0, /* width */ 5, /* mech mesh size */ 60, /* elec elem each dir */ &cell_factory, NHS, 1.0, /* mechanics solve timestep */ 1.0, /* contraction model ode dt */ "TestCardiacEmNhs2dLong"); problem.SetNoElectricsOutput(); problem.Solve(); // test by checking the length of the tissue against hardcoded value std::vector<c_vector<double,2> >& r_deformed_position = problem.rGetDeformedPosition(); TS_ASSERT_DELTA(r_deformed_position[5](0), 0.8257, 1e-3); MechanicsEventHandler::Headings(); MechanicsEventHandler::Report(); }
// longer running, finer-mesh version of TestWithCompressibleApproach() in TestCardiacElectroMechanicsProblem.hpp void TestWithCompressibleApproachLong() throw(Exception) { HeartEventHandler::Disable(); PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 2> cell_factory(-1000*1000); HeartConfig::Instance()->SetSimulationDuration(50.0); CardiacElectroMechProbRegularGeom<2> problem(COMPRESSIBLE, 0.05, /* width (cm) */ 20, /* mech mesh size*/ 5, /* elec elem each dir */ &cell_factory, KERCHOFFS2003, 1.0, /* mechanics solve timestep */ 0.01, /* Kerchoffs ode timestep */ "TestCompressibleWithKerchoffsLong"); problem.Solve(); // Mainly just testing no errors when Solve was called. // The results of this test can be visually compared with the results of the // equivalent incompressible simulation in TestWithKerchoffs. TS_ASSERT_DELTA(problem.rGetDeformedPosition()[20](0), 0.0438, 0.0002); TS_ASSERT_DELTA(problem.rGetDeformedPosition()[20](1),-0.0032, 0.0002); }
void TestWithKerchoffs() throw(Exception) { PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 2> cell_factory(-1000*1000); HeartConfig::Instance()->SetSimulationDuration(20.0); CardiacElectroMechProbRegularGeom<2> problem(INCOMPRESSIBLE, 0.05, /* width (cm) */ 1, /* mech mesh size*/ 5, /* elec elem each dir */ &cell_factory, KERCHOFFS2003, 1.0, /* mechanics solve timestep */ 0.01, /* Kerchoffs ode timestep */ "TestCardiacEmWithKerchoffs"); c_vector<double,2> pos; pos(0) = 0.05; pos(1) = 0.0; problem.SetWatchedPosition(pos); problem.SetNoElectricsOutput(); problem.Initialise(); problem.Solve(); //visualise to verify // hardcoded result TS_ASSERT_EQUALS(problem.mWatchedMechanicsNodeIndex, 1u); TS_ASSERT_DELTA(problem.rGetDeformedPosition()[1](0), 0.0479, 0.0002); TS_ASSERT_DELTA(problem.rGetDeformedPosition()[1](1),-0.0003, 0.0002); }
void TestWithCompressibleApproach() throw(Exception) { PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 2> cell_factory(-1000*1000); HeartConfig::Instance()->SetSimulationDuration(20.0); CardiacElectroMechProbRegularGeom<2> problem(COMPRESSIBLE, 0.05, /* width (cm) */ 1, /* mech mesh size*/ 5, /* elec elem each dir */ &cell_factory, KERCHOFFS2003, 1.0, /* mechanics solve timestep */ 0.01, /* Kerchoffs ode timestep */ "TestCompressibleWithKerchoffs"); problem.Solve(); // Mainly just testing no errors when Solve was called. // The results of this test can be visually compared with the results of the // equivalent incompressible simulation in TestWithKerchoffs. TS_ASSERT_DELTA(problem.rGetDeformedPosition()[1](0), 0.0472, 0.0002); TS_ASSERT_DELTA(problem.rGetDeformedPosition()[1](1),-0.0012, 0.0002); // create and initialise an incompressible NASH2004 problem, just for coverage.. HeartConfig::Instance()->SetSimulationDuration(20.0); CardiacElectroMechProbRegularGeom<2> problem2(COMPRESSIBLE,0.05,1,5,&cell_factory, NASH2004, 1.0, 0.01,""); problem2.Initialise(); }
void Test2dVariableFibres() throw(Exception) { PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 2> cell_factory(-1000*1000); TetrahedralMesh<2,2> electrics_mesh; electrics_mesh.ConstructRegularSlabMesh(1.0/96.0/*stepsize*/, 1.0/*length*/, 1.0/*width*/, 1.0/*depth*/); QuadraticMesh<2> mechanics_mesh; mechanics_mesh.ConstructRegularSlabMesh(0.2, 1.0, 1.0, 1.0 /*as above with a different stepsize*/); std::vector<unsigned> fixed_nodes = NonlinearElasticityTools<2>::GetNodesByComponentValue(mechanics_mesh, 0, 0.0); // all the X=0.0 nodes ElectroMechanicsProblemDefinition<2> problem_defn(mechanics_mesh); problem_defn.SetContractionModel(NHS,1.0); problem_defn.SetUseDefaultCardiacMaterialLaw(INCOMPRESSIBLE); problem_defn.SetZeroDisplacementNodes(fixed_nodes); problem_defn.SetMechanicsSolveTimestep(1.0); FileFinder finder("heart/test/data/fibre_tests/5by5mesh_curving_fibres.ortho",RelativeTo::ChasteSourceRoot); problem_defn.SetVariableFibreSheetDirectionsFile(finder, false); HeartConfig::Instance()->SetSimulationDuration(125.0); CardiacElectroMechanicsProblem<2,1> problem(INCOMPRESSIBLE, MONODOMAIN, &electrics_mesh, &mechanics_mesh, &cell_factory, &problem_defn, "TestCardiacEmVaryingFibres"); // // fibres going from (1,0) at X=0 to (1,1)-direction at X=1 // // the fibres file was created with the code (inside a class that owns a mesh) // for(unsigned elem_index=0; elem_index<mechanics_mesh.GetNumElements(); elem_index++) // { // double X = mechanics_mesh.GetElement(elem_index)->CalculateCentroid()[0]; // double theta = M_PI*X/4; // std::cout << cos(theta) << " " << sin(theta) << " " << -sin(theta) << " " << cos(theta) << "\n" << std::flush; // } // assert(0); // problem.SetNoElectricsOutput(); problem.Solve(); // test by checking the length of the tissue against hardcoded value std::vector<c_vector<double,2> >& r_deformed_position = problem.rGetDeformedPosition(); // visualised, looks good - contracts in X-direction near the fixed surface, // but on the other side the fibres are in the (1,1) direction, so contraction // pulls the tissue downward a bit TS_ASSERT_DELTA(r_deformed_position[5](0), 0.9055, 2e-3); //IntelProduction differs by about 1.6e-3... MechanicsEventHandler::Headings(); MechanicsEventHandler::Report(); }
void TestBathIntracellularStimulation() throw (Exception) { HeartConfig::Instance()->SetSimulationDuration(10.0); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainBath1d"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath_1d"); c_vector<double,1> centre; centre(0) = 0.5; BathCellFactory<1> cell_factory(-1e6, centre); // stimulates x=0.5 node BidomainWithBathProblem<1> bidomain_problem( &cell_factory ); TrianglesMeshReader<1,1> reader("mesh/test/data/1D_0_to_1_100_elements"); TetrahedralMesh<1,1> mesh; mesh.ConstructFromMeshReader(reader); // set the x<0.25 and x>0.75 regions as the bath region for(unsigned i=0; i<mesh.GetNumElements(); i++) { double x = mesh.GetElement(i)->CalculateCentroid()[0]; if( (x<0.25) || (x>0.75) ) { mesh.GetElement(i)->SetAttribute(HeartRegionCode::GetValidBathId()); } } bidomain_problem.SetMesh(&mesh); bidomain_problem.Initialise(); bidomain_problem.Solve(); Vec sol = bidomain_problem.GetSolution(); ReplicatableVector sol_repl(sol); // test V = 0 for all bath nodes for(unsigned i=0; i<mesh.GetNumNodes(); i++) { if(HeartRegionCode::IsRegionBath( mesh.GetNode(i)->GetRegion() )) // bath { TS_ASSERT_DELTA(sol_repl[2*i], 0.0, 1e-12); } } // test symmetry of V and phi_e for(unsigned i=0; i<=(mesh.GetNumNodes()-1)/2; i++) { unsigned opposite = mesh.GetNumNodes()-i-1; TS_ASSERT_DELTA(sol_repl[2*i], sol_repl[2*opposite], 2e-3); // V TS_ASSERT_DELTA(sol_repl[2*i+1], sol_repl[2*opposite+1], 2e-3); // phi_e } // a couple of hardcoded values TS_ASSERT_DELTA(sol_repl[2*50], 3.7684, 1e-3); TS_ASSERT_DELTA(sol_repl[2*70], 5.1777, 1e-3); }
// Build a linked list and print it out item * ll_sanity_check() { item * head = cell_factory(0); item * tmp = NULL; item * curr = head; int i; for( i=1; i<10; i++) { tmp = cell_factory(i); tmp->next = NULL; curr->next = tmp; curr = curr->next; } printf("This should print from 0 to 9\n"); print_list(head); printf("-----------------------------\n"); return head; }
void Test3dBathIntracellularStimulation() { HeartConfig::Instance()->SetSimulationDuration(1); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainBath3d"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath_3d"); c_vector<double,3> centre; centre(0) = 0.05; centre(1) = 0.05; centre(2) = 0.05; BathCellFactory<3> cell_factory(-2.5e7, centre); // stimulates x=0.05 node BidomainProblem<3> bidomain_problem( &cell_factory, true ); TetrahedralMesh<3,3> mesh; mesh.ConstructRegularSlabMesh(0.01, 0.1, 0.1, 0.1); // Set everything outside a central sphere (radius 0.4) to be bath for (unsigned i=0; i<mesh.GetNumElements(); i++) { double x = mesh.GetElement(i)->CalculateCentroid()[0]; double y = mesh.GetElement(i)->CalculateCentroid()[1]; double z = mesh.GetElement(i)->CalculateCentroid()[2]; if (sqrt((x-0.05)*(x-0.05) + (y-0.05)*(y-0.05) + (z-0.05)*(z-0.05)) > 0.04) { mesh.GetElement(i)->SetAttribute(HeartRegionCode::GetValidBathId()); } } bidomain_problem.SetMesh(&mesh); bidomain_problem.Initialise(); bidomain_problem.Solve(); Vec sol = bidomain_problem.GetSolution(); ReplicatableVector sol_repl(sol); // test V = 0 for all bath nodes for (unsigned i=0; i<mesh.GetNumNodes(); i++) { if (HeartRegionCode::IsRegionBath( mesh.GetNode(i)->GetRegion() )) // bath { TS_ASSERT_DELTA(sol_repl[2*i], 0.0, 1e-12); } } // a hardcoded value TS_ASSERT_DELTA(sol_repl[2*404], 39.6833, 1e-3); }
void TestSimulation() throw(Exception) { HeartConfig::Instance()->SetSimulationDuration(5.0); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainTutorialWithBathAndFibres"); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); /* Bath problems seem to require decreased ODE timesteps. We use the * Backward Euler version of the Luo-Rudy model (see below) instead to * improve code performance. */ HeartConfig::Instance()->SetOdeTimeStep(0.01); //ms /* Use the {{{PlaneStimulusCellFactory}}} to define a set of Luo-Rudy cells, in this * case with a Backward Euler solver. We pass the stimulus magnitude as 0.0 * as we don't want any stimulated cells. */ PlaneStimulusCellFactory<CellLuoRudy1991FromCellMLBackwardEuler,2> cell_factory(0.0); /* * Note that in the previous bath example, a mesh was read in and elements where then set to be * bath elements in the test. With fibres as well, in a bath simulation, it is better to read in a * mesh that has all the information: this mesh has bath elements defined as an extra column in the * .ele file, and a .ortho file which defines the fibre direction for each element. Note that the * .ortho file should include fibre information for bath elements as well, but they won't be used * in the simulation. (The fibres read here are the same 'kinked' fibres as in the previous fibre * tutorial). */ HeartConfig::Instance()->SetMeshFileName("mesh/test/data/2D_0_to_1mm_800_elements_bath_sides", cp::media_type::Orthotropic); /* Set anistropic conductivities. */ HeartConfig::Instance()->SetIntracellularConductivities(Create_c_vector(1.75, 0.175)); HeartConfig::Instance()->SetExtracellularConductivities(Create_c_vector(7.0, 0.7)); /* and now we define the electrodes.. */ double magnitude = -9.0e3; // uA/cm^2 double start_time = 0.0; double duration = 2; //ms HeartConfig::Instance()->SetElectrodeParameters(false, 0, magnitude, start_time, duration); /* Now create the problem class, using the cell factory and passing * in `true` as the second argument to indicate we are solving a bath * problem, and solve. */ BidomainProblem<2> bidomain_problem( &cell_factory, true ); bidomain_problem.Initialise(); bidomain_problem.Solve(); }
//////////////////////////////////////////////////////////// // Compare Mono and Bidomain Simulations //////////////////////////////////////////////////////////// void TestCompareBidomainProblemWithMonodomain3D() { // the bidomain equations reduce to the monodomain equations // if sigma_e is infinite (equivalent to saying the extra_cellular // space is grounded. sigma_e is set to be very large here: HeartConfig::Instance()->SetIntracellularConductivities(Create_c_vector(1.75, 1.75, 1.75)); HeartConfig::Instance()->SetExtracellularConductivities(Create_c_vector(17500, 17500, 17500)); HeartConfig::Instance()->SetSimulationDuration(1.0); //ms HeartConfig::Instance()->SetMeshFileName("mesh/test/data/3D_0_to_1mm_6000_elements"); HeartConfig::Instance()->SetOutputDirectory("Monodomain3d"); HeartConfig::Instance()->SetOutputFilenamePrefix("monodomain3d"); /////////////////////////////////////////////////////////////////// // monodomain /////////////////////////////////////////////////////////////////// PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 3> cell_factory(-600.0*1000); MonodomainProblem<3> monodomain_problem( &cell_factory ); monodomain_problem.Initialise(); monodomain_problem.Solve(); /////////////////////////////////////////////////////////////////// // bidomain /////////////////////////////////////////////////////////////////// HeartConfig::Instance()->SetOutputDirectory("Bidomain3d"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain3d"); BidomainProblem<3> bidomain_problem( &cell_factory ); bidomain_problem.Initialise(); bidomain_problem.Solve(); /////////////////////////////////////////////////////////////////// // compare /////////////////////////////////////////////////////////////////// DistributedVector monodomain_voltage = monodomain_problem.GetSolutionDistributedVector(); DistributedVector bidomain_solution = bidomain_problem.GetSolutionDistributedVector(); DistributedVector::Stripe bidomain_voltage(bidomain_solution,0); DistributedVector::Stripe extracellular_potential(bidomain_solution,1); for (DistributedVector::Iterator index = bidomain_solution.Begin(); index != bidomain_solution.End(); ++index) { TS_ASSERT_DELTA(monodomain_voltage[index], bidomain_voltage[index], 0.5); TS_ASSERT_DELTA(extracellular_potential[index], 0, 1.0); } }
void Test3d() throw(Exception) { PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 3> cell_factory(-1000*1000); // set up two meshes of 1mm by 1mm by 1mm TetrahedralMesh<3,3> electrics_mesh; electrics_mesh.ConstructRegularSlabMesh(0.01, 0.1, 0.1, 0.1); QuadraticMesh<3> mechanics_mesh(0.1, 0.1, 0.1, 0.1); // fix the nodes on x=0 std::vector<unsigned> fixed_nodes = NonlinearElasticityTools<3>::GetNodesByComponentValue(mechanics_mesh,0,0); HeartConfig::Instance()->SetSimulationDuration(50.0); ElectroMechanicsProblemDefinition<3> problem_defn(mechanics_mesh); problem_defn.SetContractionModel(NHS,1.0); problem_defn.SetUseDefaultCardiacMaterialLaw(INCOMPRESSIBLE); problem_defn.SetZeroDisplacementNodes(fixed_nodes); problem_defn.SetMechanicsSolveTimestep(1.0); CardiacElectroMechanicsProblem<3,1> problem(INCOMPRESSIBLE, MONODOMAIN, &electrics_mesh, &mechanics_mesh, &cell_factory, &problem_defn, "TestCardiacElectroMech3d"); problem.SetNoElectricsOutput(); problem.Solve(); // test by checking the length of the tissue against hardcoded value c_vector<double,3> undeformed_node_1 = mechanics_mesh.GetNode(1)->rGetLocation(); TS_ASSERT_DELTA(undeformed_node_1(0), 0.1, 1e-6); TS_ASSERT_DELTA(undeformed_node_1(1), 0.0, 1e-6); TS_ASSERT_DELTA(undeformed_node_1(2), 0.0, 1e-6); std::vector<c_vector<double,3> >& r_deformed_position = problem.rGetDeformedPosition(); TS_ASSERT_DELTA(r_deformed_position[1](0), 0.0879, 1e-3); MechanicsEventHandler::Headings(); MechanicsEventHandler::Report(); }
void Test2dBathIntracellularStimulation() throw (Exception) { HeartConfig::Instance()->SetSimulationDuration(1.0); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainBath2d"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath_2d"); c_vector<double,2> centre; centre(0) = 0.05; centre(1) = 0.05; BathCellFactory<2> cell_factory(-5e6, centre); // stimulates x=0.05 node BidomainWithBathProblem<2> bidomain_problem( &cell_factory ); DistributedTetrahedralMesh<2,2>* p_mesh = Load2dMeshAndSetCircularTissue<DistributedTetrahedralMesh<2,2> >( "mesh/test/data/2D_0_to_1mm_400_elements", 0.05, 0.05, 0.04); bidomain_problem.SetMesh(p_mesh); bidomain_problem.Initialise(); bidomain_problem.Solve(); Vec sol = bidomain_problem.GetSolution(); ReplicatableVector sol_repl(sol); // test V = 0 for all bath nodes for (AbstractTetrahedralMesh<2,2>::NodeIterator iter=p_mesh->GetNodeIteratorBegin(); iter != p_mesh->GetNodeIteratorEnd(); ++iter) { if (HeartRegionCode::IsRegionBath( (*iter).GetRegion() )) // bath { unsigned index=(*iter).GetIndex(); TS_ASSERT_DELTA(sol_repl[2*index], 0.0, 1e-12); } } // A couple of hardcoded values. We would normally have to look up the index in the // permutation vector when using a DistributedTetrahedralMesh, however the call to // Load2dMeshAndSetCircularTissue above imposes a DUMB partition, so no need. TS_ASSERT_DELTA(sol_repl[2*50], 28.3912, 1e-3); // node 50 TS_ASSERT_DELTA(sol_repl[2*70], 28.3912, 1e-3); // node 70 delete p_mesh; }
void TestProblemChecksUsingBathWithMultipleBathConductivities() { TrianglesMeshReader<2,2> reader("mesh/test/data/2D_0_to_1mm_400_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(reader); std::set<unsigned> tissue_ids; tissue_ids.insert(0); std::set<unsigned> bath_ids; bath_ids.insert(1); bath_ids.insert(2); // non-default identifier! HeartConfig::Instance()->SetTissueAndBathIdentifiers(tissue_ids, bath_ids); BathCellFactory<2> cell_factory( 0.0, Create_c_vector(0.0, 0.0) ); BidomainProblem<2> bidomain_problem( &cell_factory ); // non-bath problem, despite specifying bath stuff above! bidomain_problem.SetMesh( &mesh ); TS_ASSERT_THROWS_THIS( bidomain_problem.Initialise() , "User has set bath identifiers, but the BidomainProblem isn't expecting a bath. Did you mean to use BidomainProblem(..., true)? Or alternatively, BidomainWithBathProblem(...)?"); }
void TestSwitchesOffAtCorrectTime() throw(Exception) { // zero stim cell factory c_vector<double,2> centre; centre(0) = 0.05; // cm centre(1) = 0.05; // cm BathCellFactory<2> cell_factory( 0.0, centre); // boundary flux for Phi_e. -10e3 is under threshold, -14e3 crashes the cell model // // Will use printing dt = 0.01 in second run below, so choose start and end times of the // electrode which don't coincide with printing times double boundary_flux = -11.0e3; double start_time = 0.5; double duration = 2.001; // of the stimulus, in ms HeartConfig::Instance()->SetOutputDirectory("ElectrodesSwitchOffAtCorrectTime"); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetSimulationDuration(5.0); //ms ////////////////////////////////////////////////////// // solve with printing_dt = 0.01 ////////////////////////////////////////////////////// HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(0.001, 0.01, 0.01); //ms BidomainWithBathProblem<2> bidomain_problem1( &cell_factory ); TetrahedralMesh<2,2>* p_mesh1 = Load2dMeshAndSetCircularTissue<TetrahedralMesh<2,2> >( "mesh/test/data/2D_0_to_1mm_400_elements", 0.05, 0.05, 0.02); HeartConfig::Instance()->SetElectrodeParameters(false, 0, boundary_flux, start_time, duration); bidomain_problem1.SetMesh(p_mesh1); bidomain_problem1.PrintOutput(false); bidomain_problem1.Initialise(); TS_ASSERT_THROWS_THIS(bidomain_problem1.Solve(), "Additional times are now deprecated. Use only to check whether the given times are met: " "e.g. Electrode events should only happen on printing steps."); delete p_mesh1; }
/* NOTE: This test has a twin in heart/test/tutorials/TestCardiacElectroMechanicsTutorial.hpp * TestCardiacElectroMechanicsTutorial::dontTestTwistingCube() * * If you need to re-generate the fibres for this test * * Remove "dont" from the tutorial * * Rerun it * * Copy output cp /tmp/$USER/testoutput/TutorialFibreFiles/5by5by5_fibres.orthoquad heart/test/data/fibre_tests/5by5by5_fibres_by_quadpt.orthoquad */ void TestTwistingCube() throw(Exception) { PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 3> cell_factory(-1000*1000); // set up two meshes of 1mm by 1mm by 1mm TetrahedralMesh<3,3> electrics_mesh; electrics_mesh.ConstructRegularSlabMesh(0.01, 0.1, 0.1, 0.1); QuadraticMesh<3> mechanics_mesh(0.02, 0.1, 0.1, 0.1); // fix the nodes on Z=0 std::vector<unsigned> fixed_nodes = NonlinearElasticityTools<3>::GetNodesByComponentValue(mechanics_mesh,2,0.0); HeartConfig::Instance()->SetSimulationDuration(50.0); ElectroMechanicsProblemDefinition<3> problem_defn(mechanics_mesh); problem_defn.SetContractionModel(KERCHOFFS2003,1.0); problem_defn.SetUseDefaultCardiacMaterialLaw(INCOMPRESSIBLE); problem_defn.SetZeroDisplacementNodes(fixed_nodes); problem_defn.SetMechanicsSolveTimestep(1.0); FileFinder finder("heart/test/data/fibre_tests/5by5by5_fibres_by_quadpt.orthoquad",RelativeTo::ChasteSourceRoot); problem_defn.SetVariableFibreSheetDirectionsFile(finder, true); CardiacElectroMechanicsProblem<3,1> problem(INCOMPRESSIBLE, MONODOMAIN, &electrics_mesh, &mechanics_mesh, &cell_factory, &problem_defn, "TestCardiacElectroMech3dTwistingCube"); problem.Solve(); // verified that it twists by visualising, some hardcoded values here.. { //Check that we are indexing the relevant corners of the cube c_vector<double, 3> undeformed_node1 = mechanics_mesh.GetNode(6*6*5)->rGetLocation(); TS_ASSERT_DELTA(undeformed_node1(0), 0.0, 1e-6); TS_ASSERT_DELTA(undeformed_node1(1), 0.0, 1e-6); TS_ASSERT_DELTA(undeformed_node1(2), 0.1, 1e-6); c_vector<double, 3> undeformed_node2 = mechanics_mesh.GetNode(6*6*6-1)->rGetLocation(); TS_ASSERT_DELTA(undeformed_node2(0), 0.1, 1e-6); TS_ASSERT_DELTA(undeformed_node2(1), 0.1, 1e-6); TS_ASSERT_DELTA(undeformed_node2(2), 0.1, 1e-6); } std::vector<c_vector<double,3> >& r_deformed_position = problem.rGetDeformedPosition(); TS_ASSERT_DELTA(r_deformed_position[6*6*5](0), 0.0116, 1e-3); TS_ASSERT_DELTA(r_deformed_position[6*6*5](1), -0.0141, 1e-3); TS_ASSERT_DELTA(r_deformed_position[6*6*5](2), 0.1007, 1e-3); TS_ASSERT_DELTA(r_deformed_position[6*6*6-1](0), 0.0872, 1e-3); TS_ASSERT_DELTA(r_deformed_position[6*6*6-1](1), 0.1138, 1e-3); TS_ASSERT_DELTA(r_deformed_position[6*6*6-1](2), 0.1015, 1e-3); MechanicsEventHandler::Headings(); MechanicsEventHandler::Report(); }
// // BAD test - fails with HYPRE (for some reason HYPRE can't solve the one of the linear systems, and // the search direction in the end doesn't decrease the residual), and also with ILU if you increase // the number of elements (whether LR91 or N98 is used). Probably the active tension is too high. // // Now removed // void removedTestExplicitSolverWithNash2004() throw(Exception) { #ifdef MECH_USE_HYPRE TS_FAIL("This test is known to fail with HYPRE - see comments in test"); return; #endif PlaneStimulusCellFactory<CML_noble_varghese_kohl_noble_1998_basic_with_sac, 2> cell_factory(-1000*1000); HeartConfig::Instance()->SetSimulationDuration(20.0); CardiacElectroMechProbRegularGeom<2> problem(INCOMPRESSIBLE, 0.05, /* width (cm) */ 1, /* mech mesh size*/ 5, /* elec elem each dir */ &cell_factory, NASH2004, 1.0, /* mechanics solve timestep */ 0.01, /* nash ode timestep */ "TestExplicitWithNash"); c_vector<double,2> pos; pos(0) = 0.05; pos(1) = 0.0; problem.SetWatchedPosition(pos); problem.SetNoElectricsOutput(); problem.Initialise(); problem.Solve(); //visualise to verify // hardcoded result TS_ASSERT_EQUALS(problem.mWatchedMechanicsNodeIndex, 1u); TS_ASSERT_DELTA(problem.rGetDeformedPosition()[1](0), 0.0419, 0.0002); }
// In this test we have no cardiac tissue, so that the equations are just sigma * phi_e''=0 // throughout the domain (with a Neumann boundary condition on x=1 and a dirichlet boundary // condition (ie grounding) on x=0), so the exact solution can be calculated and compared // against. void Test1dProblemOnlyBathGroundedOneSide() throw (Exception) { HeartConfig::Instance()->SetSimulationDuration(0.5); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainBathOnlyBath"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath"); c_vector<double,1> centre; centre(0) = 0.5; BathCellFactory<1> cell_factory(-1e6, centre); TrianglesMeshReader<1,1> reader("mesh/test/data/1D_0_to_1_10_elements"); TetrahedralMesh<1,1> mesh; mesh.ConstructFromMeshReader(reader); for(unsigned i=0; i<mesh.GetNumElements(); i++) { mesh.GetElement(i)->SetAttribute(HeartRegionCode::GetValidBathId()); } // create boundary conditions container double boundary_val = 1.0; boost::shared_ptr<BoundaryConditionsContainer<1,1,2> > p_bcc(new BoundaryConditionsContainer<1,1,2>); ConstBoundaryCondition<1>* p_bc_stim = new ConstBoundaryCondition<1>(boundary_val); ConstBoundaryCondition<1>* p_zero_stim = new ConstBoundaryCondition<1>(0.0); // loop over boundary elements and set (sigma\gradphi).n = 1.0 on RHS edge for(TetrahedralMesh<1,1>::BoundaryElementIterator iter = mesh.GetBoundaryElementIteratorBegin(); iter != mesh.GetBoundaryElementIteratorEnd(); iter++) { if (((*iter)->GetNodeLocation(0))[0]==1.0) { /// \todo: I think you need to provide a boundary condition for unknown#1 if you are gonig to provide one for unknown#2? p_bcc->AddNeumannBoundaryCondition(*iter, p_zero_stim, 0); p_bcc->AddNeumannBoundaryCondition(*iter, p_bc_stim, 1); } } BidomainWithBathProblem<1> bidomain_problem( &cell_factory ); bidomain_problem.SetBoundaryConditionsContainer(p_bcc); bidomain_problem.SetMesh(&mesh); bidomain_problem.Initialise(); // fix phi=0 on LHS edge std::vector<unsigned> fixed_nodes; fixed_nodes.push_back(0); bidomain_problem.SetFixedExtracellularPotentialNodes(fixed_nodes); bidomain_problem.Solve(); Vec sol = bidomain_problem.GetSolution(); ReplicatableVector sol_repl(sol); // test phi = x*boundary_val/sigma (solution of phi''=0, phi(0)=0, sigma*phi'(1)=boundary_val for(unsigned i=0; i<mesh.GetNumNodes(); i++) { double bath_cond = HeartConfig::Instance()->GetBathConductivity(); double x = mesh.GetNode(i)->rGetLocation()[0]; TS_ASSERT_DELTA(sol_repl[2*i], 0.0, 1e-12); // V TS_ASSERT_DELTA(sol_repl[2*i+1], x*boundary_val/bath_cond, 1e-4); // phi_e } }
// These tests are older than the above tests.. void TestImplicitNhs2dOneMechanicsElement() throw(Exception) { PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 2> cell_factory(-1000*1000); HeartConfig::Instance()->SetSimulationDuration(10.0); CardiacElectroMechProbRegularGeom<2> problem(INCOMPRESSIBLE, 0.05, /* width (cm) */ 1, /* mech mesh size*/ 5, /* elec elem each dir */ &cell_factory, NHS, 1.0, /* mechanics solve timestep */ 0.01, /* contraction model ode timestep */ "TestCardiacElectroMechOneElement"); c_vector<double,2> pos; pos(0) = 0.05; pos(1) = 0.0; problem.SetWatchedPosition(pos); TS_ASSERT_THROWS_CONTAINS(problem.SetOutputDeformationGradientsAndStress(3.4),"not a multiple"); problem.SetOutputDeformationGradientsAndStress(3.0); problem.Solve(); // test by checking the length of the tissue against hardcoded value std::vector<c_vector<double,2> >& r_deformed_position = problem.rGetDeformedPosition(); TS_ASSERT_DELTA(r_deformed_position[1](0), 0.0497, 1e-4); OutputFileHandler handler("TestCardiacElectroMechOneElement",false); NumericFileComparison comparer(handler.GetOutputDirectoryFullPath() + "watched.txt","heart/test/data/good_watched.txt"); TS_ASSERT(comparer.CompareFiles(1e-2)); FileFinder electrics_dir = handler.FindFile("electrics"); TS_ASSERT(electrics_dir.IsDir()); // check electrics output was written TS_ASSERT(handler.FindFile("deformation/deformation_gradient_0.strain").Exists()); TS_ASSERT(handler.FindFile("deformation/deformation_gradient_3.strain").Exists()); TS_ASSERT(handler.FindFile("deformation/deformation_gradient_6.strain").Exists()); TS_ASSERT(handler.FindFile("deformation/second_PK_0.stress").Exists()); TS_ASSERT(handler.FindFile("deformation/second_PK_3.stress").Exists()); TS_ASSERT(handler.FindFile("deformation/second_PK_6.stress").Exists()); // coverage HeartConfig::Instance()->SetSimulationDuration(10.0); // has to be reset after a solve, it seems.. // We can now #2370 put any model anywhere, so these checks don't make sense... // CardiacElectroMechProbRegularGeom<2> prob_with_bad_model(INCOMPRESSIBLE,0.05,1,5,&cell_factory,NONPHYSIOL1,1,0.01,""); // TS_ASSERT_THROWS_CONTAINS(prob_with_bad_model.Solve(),"Invalid contraction model"); // // CardiacElectroMechProbRegularGeom<2> prob_with_bad_model_comp(COMPRESSIBLE,0.05,1,5,&cell_factory,NONPHYSIOL1,1,0.01,""); // TS_ASSERT_THROWS_CONTAINS(prob_with_bad_model_comp.Solve(),"Invalid contraction model"); CardiacElectroMechProbRegularGeom<2> prob_with_bad_timesteps(INCOMPRESSIBLE,0.05,1,5,&cell_factory,NHS,0.025,0.01,""); TS_ASSERT_THROWS_CONTAINS(prob_with_bad_timesteps.Initialise(),"does not divide"); MechanicsEventHandler::Headings(); MechanicsEventHandler::Report(); }
// see #1061 void Test3dBathExtracellularStimulusOneEdgeGroundedOnOppositeEdge() { HeartConfig::Instance()->SetSimulationDuration(6); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainBath3dExtraStimGrounded"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath_3d"); HeartConfig::Instance()->SetOdeTimeStep(0.005); //ms // need to create a cell factory but don't want any intra stim, so magnitude // of stim is zero. c_vector<double,3> centre; centre(0) = 0.1; centre(1) = 0.1; centre(2) = 0.1; BathCellFactory<3> cell_factory( 0.0, centre); BidomainProblem<3> bidomain_problem( &cell_factory, true ); TrianglesMeshReader<3,3> reader("mesh/test/data/cube_2mm_1016_elements"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(reader); // Set everything outside a central sphere (radius 0.4) to be bath for (unsigned i=0; i<mesh.GetNumElements(); i++) { double x = mesh.GetElement(i)->CalculateCentroid()[0]; double y = mesh.GetElement(i)->CalculateCentroid()[1]; double z = mesh.GetElement(i)->CalculateCentroid()[2]; if (sqrt((x-0.1)*(x-0.1) + (y-0.1)*(y-0.1) + (z-0.1)*(z-0.1)) > 0.03) { mesh.GetElement(i)->SetAttribute(1); } } bidomain_problem.SetMesh(&mesh); //boundary flux for Phi_e double boundary_flux = -4e3; double duration = 2.5; //ms HeartConfig::Instance()->SetElectrodeParameters(true,0,boundary_flux, 0.0, duration); bidomain_problem.Initialise(); bidomain_problem.Solve(); Vec sol = bidomain_problem.GetSolution(); ReplicatableVector sol_repl(sol); bool ap_triggered = false; for (unsigned i=0; i<mesh.GetNumNodes(); i++) { // test V = 0 for all bath nodes if (HeartRegionCode::IsRegionBath( mesh.GetNode(i)->GetRegion() )) // bath { TS_ASSERT_DELTA(sol_repl[2*i], 0.0, 1e-12); } else if (sol_repl[2*i] > 0.0) { ap_triggered = true; } } TS_ASSERT(ap_triggered); }
// see #1061 void Test2dBathExtracellularStimulusOneEdgeGroundedOnOppositeEdge() { HeartConfig::Instance()->SetSimulationDuration(40); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainBath2dExtraStimGrounded"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath_2d"); HeartConfig::Instance()->SetOdeTimeStep(0.005); //ms // need to create a cell factory but don't want any intra stim, so magnitude // of stim is zero. c_vector<double,2> centre; centre(0) = 0.05; centre(1) = 0.05; BathCellFactory<2> cell_factory( 0.0, centre); BidomainProblem<2> bidomain_problem( &cell_factory, true ); TrianglesMeshReader<2,2> reader("mesh/test/data/2D_0_to_1mm_400_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(reader); // Set everything outside a central circle (radius 0.4) to be bath for (unsigned i=0; i<mesh.GetNumElements(); i++) { double x = mesh.GetElement(i)->CalculateCentroid()[0]; double y = mesh.GetElement(i)->CalculateCentroid()[1]; if (sqrt((x-0.05)*(x-0.05) + (y-0.05)*(y-0.05)) > 0.02) { mesh.GetElement(i)->SetAttribute(HeartRegionCode::GetValidBathId()); } } bidomain_problem.SetMesh(&mesh); //boundary flux for Phi_e //-4e3 is enough to trigger an action potential, -3e3 is below threshold, -5e3 crashes the cell model. double boundary_flux = -9e3; double duration = 2.5; //ms HeartConfig::Instance()->SetElectrodeParameters(true,0,boundary_flux, 0.0, duration); bidomain_problem.Initialise(); bidomain_problem.Solve(); Vec sol = bidomain_problem.GetSolution(); ReplicatableVector sol_repl(sol); bool ap_triggered = false; /* * We are checking the last time step. This test will only make sure that an upstroke is triggered. * We ran longer simulation for 350 ms and a nice AP was observed. */ for (unsigned i=0; i<mesh.GetNumNodes(); i++) { // test V = 0 for all bath nodes if (mesh.GetNode(i)->GetRegion()==1) // bath { TS_ASSERT_DELTA(sol_repl[2*i], 0.0, 1e-12); } else if (sol_repl[2*i] > 0.0) { ap_triggered = true; } } TS_ASSERT(ap_triggered); }
void Test2dBathInputFluxEqualsOutputFlux() throw (Exception) { HeartConfig::Instance()->SetSimulationDuration(3.0); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainBath2dFluxCompare"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath_2d_fluxes"); // Coverage of Hdf5ToCmguiConverter with bath HeartConfig::Instance()->SetVisualizeWithCmgui(true); HeartConfig::Instance()->SetOdeTimeStep(0.001); //ms // need to create a cell factory but don't want any intra stim, so magnitude // of stim is zero. c_vector<double,2> centre; centre(0) = 0.05; // cm centre(1) = 0.05; // cm BathCellFactory<2> cell_factory( 0.0, centre); BidomainWithBathProblem<2> bidomain_problem( &cell_factory ); // Coverage TS_ASSERT(bidomain_problem.GetHasBath()); TetrahedralMesh<2,2>* p_mesh = Load2dMeshAndSetCircularTissue<TetrahedralMesh<2,2> >( "mesh/test/data/2D_0_to_1mm_400_elements", 0.05, 0.05, 0.02); //boundary flux for Phi_e. -10e3 is under threshold, -14e3 crashes the cell model double boundary_flux = -11.0e3; double start_time = 0.5; double duration = 1.9; // of the stimulus, in ms HeartConfig::Instance()->SetElectrodeParameters(false,0, boundary_flux, start_time, duration); bidomain_problem.SetMesh(p_mesh); bidomain_problem.Initialise(); bidomain_problem.Solve(); Vec sol = bidomain_problem.GetSolution(); ReplicatableVector sol_repl(sol); bool ap_triggered = false; /* * We are checking the last time step. This test will only make sure that an upstroke is triggered. * We ran longer simulation for 350 ms and a nice AP was observed. */ for (unsigned i=0; i<p_mesh->GetNumNodes(); i++) { // test V = 0 for all bath nodes and that an AP is triggered in the tissue if (HeartRegionCode::IsRegionBath( p_mesh->GetNode(i)->GetRegion() )) // bath { TS_ASSERT_DELTA(sol_repl[2*i], 0.0, 1e-12); } else if (sol_repl[2*i] > 0.0)//at the last time step { ap_triggered = true; } } TS_ASSERT_EQUALS(bidomain_problem.mpElectrodes->mAreActive, false); // should be switched off by now.. TS_ASSERT(ap_triggered); delete p_mesh; }
void Test2dBathMultipleBathConductivities() throw (Exception) { HeartConfig::Instance()->SetSimulationDuration(2.0); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainBath2dMultipleBathConductivities"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath_2d"); HeartConfig::Instance()->SetOdeTimeStep(0.001); //ms ??? std::set<unsigned> tissue_ids; tissue_ids.insert(0); // Same as default value defined in HeartConfig std::set<unsigned> bath_ids; bath_ids.insert(2); bath_ids.insert(3); bath_ids.insert(4); HeartConfig::Instance()->SetTissueAndBathIdentifiers(tissue_ids, bath_ids); // need to create a cell factory but don't want any intra stim, so magnitude // of stim is zero. c_vector<double,2> centre; centre(0) = 0.05; // cm centre(1) = 0.05; // cm BathCellFactory<2> cell_factory( 0.0, centre); BidomainWithBathProblem<2> bidomain_problem( &cell_factory ); DistributedTetrahedralMesh<2,2> mesh; mesh.ConstructRegularSlabMesh(0.05, 0.9, 0.9); // set the x<0.25 and x>0.75 regions as the bath region for (AbstractTetrahedralMesh<2,2>::ElementIterator iter = mesh.GetElementIteratorBegin(); iter != mesh.GetElementIteratorEnd(); ++iter) { double x = iter->CalculateCentroid()[0]; double y = iter->CalculateCentroid()[1]; if( (x>0.3) && (x<0.6) && (y>0.3) && (y<0.6) ) { iter->SetAttribute(0); } else { if (y<0.2) { iter->SetAttribute(2); } else if (y<0.7) { iter->SetAttribute(3); } else if (y<0.9) { iter->SetAttribute(4); } } } std::map<unsigned, double> multiple_bath_conductivities; multiple_bath_conductivities[2] = 7.0; multiple_bath_conductivities[3] = 1.0; multiple_bath_conductivities[4] = 0.001; HeartConfig::Instance()->SetBathMultipleConductivities(multiple_bath_conductivities); double boundary_flux = -3.0e3; double start_time = 0.0; double duration = 1.0; // of the stimulus, in ms HeartConfig::Instance()->SetElectrodeParameters(false, 0, boundary_flux, start_time, duration); bidomain_problem.SetMesh(&mesh); bidomain_problem.Initialise(); bidomain_problem.Solve(); DistributedVector distributed_solution = bidomain_problem.GetSolutionDistributedVector(); DistributedVector::Stripe voltage(distributed_solution, 0); /* * We are checking the last time step. This test will only make sure that an AP is triggered. */ bool ap_triggered = false; for (DistributedVector::Iterator index = distributed_solution.Begin(); index!= distributed_solution.End(); ++index) { // test V = 0 for all bath nodes and that an AP is triggered in the tissue if (HeartRegionCode::IsRegionBath( mesh.GetNode(index.Global)->GetRegion() )) // bath { TS_ASSERT_DELTA(voltage[index], 0.0, 1e-12); } else if (voltage[index] > 0.0)//at the last time step { ap_triggered = true; } } TS_ASSERT(PetscTools::ReplicateBool(ap_triggered)); }
// runs 5 different 3d tests with fibres read to be in various directions, and // checks contraction occurs in the right directions (and bulging occurs in the // other directions) void TestFibreRead() throw(Exception) { PlaneStimulusCellFactory<CellLuoRudy1991FromCellML,3> cell_factory(-5000*1000); double tissue_initial_size = 0.05; TetrahedralMesh<3,3> electrics_mesh; electrics_mesh.ConstructRegularSlabMesh(0.01/*stepsize*/, tissue_initial_size/*length*/, tissue_initial_size/*width*/, tissue_initial_size); QuadraticMesh<3> mechanics_mesh; mechanics_mesh.ConstructRegularSlabMesh(0.05, tissue_initial_size, tissue_initial_size, tissue_initial_size /*as above with a different stepsize*/); std::vector<unsigned> fixed_nodes = NonlinearElasticityTools<3>::GetNodesByComponentValue(mechanics_mesh, 2, 0.0); HeartConfig::Instance()->SetSimulationDuration(10.0); ElectroMechanicsProblemDefinition<3> problem_defn(mechanics_mesh); problem_defn.SetContractionModel(KERCHOFFS2003,1.0); problem_defn.SetZeroDisplacementNodes(fixed_nodes); problem_defn.SetMechanicsSolveTimestep(1.0); problem_defn.SetUseDefaultCardiacMaterialLaw(COMPRESSIBLE); // This is how the fibres files that are used in the simulations are created. // alongX.ortho is: fibres in X axis, sheet in XY // -- same as default directions: should give shortening in X direction // and identical results to default fibre setting // alongY1.ortho is: fibres in Y axis, sheet in YX // alongY2.ortho is: fibres in Y axis, sheet in YZ // -- as material law is transversely isotropic, these two should // give identical results, shortening in Y-direction // alongZ.ortho is: fibres in Z axis, (sheet in XZ) // -- should shorten in Z-direction OutputFileHandler handler("FibreFiles"); out_stream p_X_file = handler.OpenOutputFile("alongX.ortho"); out_stream p_Y1_file = handler.OpenOutputFile("alongY1.ortho"); out_stream p_Y2_file = handler.OpenOutputFile("alongY2.ortho"); out_stream p_Z_file = handler.OpenOutputFile("alongZ.ortho"); *p_X_file << mechanics_mesh.GetNumElements() << "\n"; *p_Y1_file << mechanics_mesh.GetNumElements() << "\n"; *p_Y2_file << mechanics_mesh.GetNumElements() << "\n"; *p_Z_file << mechanics_mesh.GetNumElements() << "\n"; for(unsigned i=0; i<mechanics_mesh.GetNumElements(); i++) { //double X = mechanics_mesh.GetElement(i)->CalculateCentroid()(0); *p_X_file << "1 0 0 0 1 0 0 0 1\n"; *p_Y1_file << "0 1 0 1 0 0 0 0 1\n"; *p_Y2_file << "0 1 0 0 0 1 1 0 0\n"; *p_Z_file << "0 0 1 1 0 0 0 1 0\n"; } p_X_file->close(); p_Y1_file->close(); p_Y2_file->close(); p_Z_file->close(); ////////////////////////////////////////////////////////////////// // Solve with no fibres read. ////////////////////////////////////////////////////////////////// std::vector<c_vector<double,3> > r_deformed_position_no_fibres; { HeartConfig::Instance()->SetSimulationDuration(20.0); CardiacElectroMechanicsProblem<3,1> problem(COMPRESSIBLE, MONODOMAIN, &electrics_mesh, &mechanics_mesh, &cell_factory, &problem_defn, "TestCardiacEmFibreRead"); problem.Solve(); r_deformed_position_no_fibres = problem.rGetDeformedPosition(); } ////////////////////////////////////////////////////////////////// // Solve with fibres read: fibres in X-direction ////////////////////////////////////////////////////////////////// std::vector<c_vector<double,3> > r_deformed_position_fibres_alongX; { HeartConfig::Instance()->SetSimulationDuration(20.0); FileFinder finder("heart/test/data/fibre_tests/alongX.ortho", RelativeTo::ChasteSourceRoot); problem_defn.SetVariableFibreSheetDirectionsFile(finder, false); CardiacElectroMechanicsProblem<3,1> problem(COMPRESSIBLE, MONODOMAIN, &electrics_mesh, &mechanics_mesh, &cell_factory, &problem_defn, "TestCardiacEmFibreRead"); problem.Solve(); r_deformed_position_fibres_alongX = problem.rGetDeformedPosition(); } // test the two results are identical for(unsigned i=0; i<r_deformed_position_no_fibres.size(); i++) { for(unsigned j=0; j<3; j++) { TS_ASSERT_DELTA(r_deformed_position_no_fibres[i](j), r_deformed_position_fibres_alongX[i](j), 1e-8); } } // check node 7 starts is the far corner assert(fabs(mechanics_mesh.GetNode(7)->rGetLocation()[0] - tissue_initial_size)<1e-8); assert(fabs(mechanics_mesh.GetNode(7)->rGetLocation()[1] - tissue_initial_size)<1e-8); assert(fabs(mechanics_mesh.GetNode(7)->rGetLocation()[2] - tissue_initial_size)<1e-8); // Test that contraction occurred in the X-direction TS_ASSERT_LESS_THAN(r_deformed_position_fibres_alongX[7](0), tissue_initial_size); TS_ASSERT_LESS_THAN(tissue_initial_size, r_deformed_position_fibres_alongX[7](1)); TS_ASSERT_LESS_THAN(tissue_initial_size, r_deformed_position_fibres_alongX[7](2)); // hardcoded test to check nothing changes TS_ASSERT_DELTA(r_deformed_position_fibres_alongX[7](0), 0.0487, 1e-4); TS_ASSERT_DELTA(r_deformed_position_fibres_alongX[7](1), 0.0506, 1e-4); //////////////////////////////////////////////////////////////////// // Solve with fibres read: fibres in Y-direction, sheet in YX plane //////////////////////////////////////////////////////////////////// std::vector<c_vector<double,3> > r_deformed_position_fibres_alongY1; { HeartConfig::Instance()->SetSimulationDuration(20.0); FileFinder finder("heart/test/data/fibre_tests/alongY1.ortho", RelativeTo::ChasteSourceRoot); problem_defn.SetVariableFibreSheetDirectionsFile(finder, false); CardiacElectroMechanicsProblem<3,1> problem(COMPRESSIBLE, MONODOMAIN, &electrics_mesh, &mechanics_mesh, &cell_factory, &problem_defn, "TestCardiacEmFibreRead"); problem.Solve(); r_deformed_position_fibres_alongY1 = problem.rGetDeformedPosition(); } //////////////////////////////////////////////////////////////////// // Solve with fibres read: fibres in Y-direction, sheet in YZ plane //////////////////////////////////////////////////////////////////// std::vector<c_vector<double,3> > r_deformed_position_fibres_alongY2; { HeartConfig::Instance()->SetSimulationDuration(20.0); FileFinder fibres_file("heart/test/data/fibre_tests/alongY2.ortho", RelativeTo::ChasteSourceRoot); problem_defn.SetVariableFibreSheetDirectionsFile(fibres_file, false); CardiacElectroMechanicsProblem<3,1> problem(COMPRESSIBLE, MONODOMAIN, &electrics_mesh, &mechanics_mesh, &cell_factory, &problem_defn, "TestCardiacEmFibreRead"); problem.Solve(); r_deformed_position_fibres_alongY2 = problem.rGetDeformedPosition(); } // test the two results are identical for(unsigned i=0; i<r_deformed_position_no_fibres.size(); i++) { for(unsigned j=0; j<3; j++) { TS_ASSERT_DELTA(r_deformed_position_fibres_alongY1[i](j), r_deformed_position_fibres_alongY2[i](j), 1e-8); } } // Test that contraction occurred in the Y-direction TS_ASSERT_LESS_THAN(tissue_initial_size, r_deformed_position_fibres_alongY1[7](0)); TS_ASSERT_LESS_THAN(r_deformed_position_fibres_alongY1[7](1), tissue_initial_size); TS_ASSERT_LESS_THAN(tissue_initial_size, r_deformed_position_fibres_alongY1[7](2)); // hardcoded test to check nothing changes TS_ASSERT_DELTA(r_deformed_position_fibres_alongY1[7](1), 0.0487, 1e-4); TS_ASSERT_DELTA(r_deformed_position_fibres_alongY1[7](0), 0.0506, 1e-4); ////////////////////////////////////////////////////////////////// // Solve with fibres read: fibres in Z-direction ////////////////////////////////////////////////////////////////// std::vector<c_vector<double,3> > r_deformed_position_fibres_alongZ; { HeartConfig::Instance()->SetSimulationDuration(20.0); FileFinder finder("heart/test/data/fibre_tests/alongZ.ortho", RelativeTo::ChasteSourceRoot); problem_defn.SetVariableFibreSheetDirectionsFile(finder, false); CardiacElectroMechanicsProblem<3,1> problem(COMPRESSIBLE, MONODOMAIN, &electrics_mesh, &mechanics_mesh, &cell_factory, &problem_defn, "TestCardiacEmFibreRead"); problem.Solve(); r_deformed_position_fibres_alongZ = problem.rGetDeformedPosition(); } // Test that contraction occurred in the X-direction TS_ASSERT_LESS_THAN(tissue_initial_size, r_deformed_position_fibres_alongZ[7](0)); TS_ASSERT_LESS_THAN(tissue_initial_size, r_deformed_position_fibres_alongZ[7](1)); TS_ASSERT_LESS_THAN(r_deformed_position_fibres_alongZ[7](2), tissue_initial_size); // hardcoded test to check nothing changes TS_ASSERT_DELTA(r_deformed_position_fibres_alongZ[7](2), 0.0466, 1e-4); TS_ASSERT_DELTA(r_deformed_position_fibres_alongZ[7](0), 0.0504, 1e-4); }
void Test2dBathGroundedElectrodeStimulusSwitchesOnOff() throw (Exception) { // Total execution time is 5 ms. Electrodes are on in [1.0, 3.0] HeartConfig::Instance()->SetOutputDirectory("BidomainBath2dGroundedOnOff"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath_2d_grounded_on_off"); HeartConfig::Instance()->SetOdeTimeStep(0.001); //ms // need to create a cell factory but don't want any intra stim, so magnitude // of stim is zero. c_vector<double,2> centre; centre(0) = 0.05; // cm centre(1) = 0.05; // cm BathCellFactory<2> cell_factory( 0.0, centre); BidomainWithBathProblem<2> bidomain_problem( &cell_factory ); TetrahedralMesh<2,2>* p_mesh = Load2dMeshAndSetCircularTissue<TetrahedralMesh<2,2> >( "mesh/test/data/2D_0_to_1mm_400_elements", 0.05, 0.05, 0.02); //boundary flux for Phi_e. -10e3 is under threshold, -14e3 crashes the cell model double boundary_flux = -11.0e3; double start_time = 1.0; double duration = 2.0; // of the stimulus, in ms HeartConfig::Instance()->SetElectrodeParameters( true, 0, boundary_flux,start_time, duration ); bidomain_problem.SetMesh(p_mesh); bidomain_problem.Initialise(); /* * While t in [0.0, 1.0) electrodes are off */ { HeartConfig::Instance()->SetSimulationDuration(0.5); //ms bidomain_problem.Solve(); /// \todo: we don't need a ReplicatableVector here. Every processor can check locally Vec sol = bidomain_problem.GetSolution(); ReplicatableVector sol_repl(sol); for (unsigned i=0; i<p_mesh->GetNumNodes(); i++) { // test phi_e close to 0 for all bath nodes since electrodes are off if (HeartRegionCode::IsRegionBath( p_mesh->GetNode(i)->GetRegion() )) // bath { TS_ASSERT_DELTA(sol_repl[2*i+1], 0.0, 0.5); } } TS_ASSERT_EQUALS(bidomain_problem.mpElectrodes->mAreActive, false); // should be switched off by now.. } /* * At the end of the simulation AP has been triggered */ { HeartConfig::Instance()->SetSimulationDuration(5.0); //ms bidomain_problem.Solve(); Vec sol = bidomain_problem.GetSolution(); ReplicatableVector sol_repl(sol); bool ap_triggered = false; /* * We are checking the last time step. This test will only make sure that an upstroke is triggered. * We ran longer simulation for 350 ms and a nice AP was observed. */ for (unsigned i=0; i<p_mesh->GetNumNodes(); i++) { // test V = 0 for all bath nodes and that an AP is triggered in the tissue if (HeartRegionCode::IsRegionBath( p_mesh->GetNode(i)->GetRegion() )) // bath { TS_ASSERT_DELTA(sol_repl[2*i], 0.0, 1e-12); } else if (sol_repl[2*i] > 0.0)//at the last time step { ap_triggered = true; } } // Check that grounded electrode has been successfully removed and therefore phi_e !=0. // Nodes defining grounded electrode are 10, 21, 32, 43, 54, ... , 120 TS_ASSERT_DELTA(sol_repl[21], -80.2794, 1e-3); TS_ASSERT_DELTA(sol_repl[43], -80.2794, 1e-3); TS_ASSERT_DELTA(sol_repl[241], -80.2794, 1e-3); TS_ASSERT_EQUALS(bidomain_problem.mpElectrodes->mAreActive, false); // should be switched off by now.. TS_ASSERT(ap_triggered); } delete p_mesh; }
void TestWithBathAndElectrodes() { /* First, set the end time and output info. In this simulation * we'll explicitly read the mesh, alter it, then pass it * to the problem class, so we don't set the mesh file name. */ HeartConfig::Instance()->SetSimulationDuration(3.0); //ms HeartConfig::Instance()->SetOutputDirectory("BidomainTutorialWithBath"); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); /* Bath problems seem to require decreased ODE timesteps. */ HeartConfig::Instance()->SetOdeTimeStep(0.001); //ms /* Use the {{{PlaneStimulusCellFactory}}} to define a set * of Luo-Rudy cells. We pass the stimulus magnitude as 0.0 * as we don't want any stimulated cells. */ PlaneStimulusCellFactory<CellLuoRudy1991FromCellMLBackwardEuler,2> cell_factory(0.0); /* * Now, we load up a rectangular mesh (in triangle/tetgen format), done as follows, * using {{{TrianglesMeshReader}}}. Note that we use a distributed mesh, so the data * is shared among processes if run in parallel. */ TrianglesMeshReader<2,2> reader("mesh/test/data/2D_0_to_1mm_400_elements"); DistributedTetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(reader); /* * In most simulations there is one valid tissue identifier and one valid bath identifier * (for elements). * One of these can be assigned to an element with * * {{{mesh.GetElement(i)->SetAttribute(HeartRegionCode::GetValidTissueId());}}} * * {{{mesh.GetElement(i)->SetAttribute(HeartRegionCode::GetValidBathId());}}} * * If we want heterogeneous conductivities outside the heart (for example for torso and blood) * then we will need different identifiers: */ std::set<unsigned> tissue_ids; static unsigned tissue_id=0; tissue_ids.insert(tissue_id); std::set<unsigned> bath_ids; static unsigned bath_id1=1; bath_ids.insert(bath_id1); static unsigned bath_id2=2; bath_ids.insert(bath_id2); HeartConfig::Instance()->SetTissueAndBathIdentifiers(tissue_ids, bath_ids); /* In bath problems, each element has an attribute which must be set * to 0 (cardiac tissue) or 1 (bath). This can be done by having an * extra column in the element file (see the file formats documentation, * or for example * mesh/test/data/1D_0_to_1_10_elements_with_two_attributes.ele, * and note that the header in this file has 1 at the end to indicate that * the file defines an attribute for each element). We have read in a mesh * without this type of information set up, so we set it up manually, * by looping over elements and setting those more than 2mm from the centre * as bath elements (by default, the others are cardiac elements). */ for (AbstractTetrahedralMesh<2,2>::ElementIterator iter = mesh.GetElementIteratorBegin(); iter != mesh.GetElementIteratorEnd(); ++iter) { double x = iter->CalculateCentroid()[0]; double y = iter->CalculateCentroid()[1]; if (sqrt((x-0.05)*(x-0.05) + (y-0.05)*(y-0.05)) > 0.02) { if (y<0.05) { //Outside circle on the bottom iter->SetAttribute(bath_id1); } else { //Outside circle on the top iter->SetAttribute(bath_id2); } } else { //IDs default to 0, but we want to be safe iter->SetAttribute(tissue_id); } } /* HOW_TO_TAG Cardiac/Problem definition * Tell Chaste that a mesh has been modified * * Since we have modified the mesh by setting element attributes, we need to inform Chaste of this fact. * If we do not, problems will arise when [wiki:UserTutorials/CardiacCheckpointingAndRestarting checkpointing], * since the code that saves the simulation state will assume that it can just reuse the original mesh files, * and thus won't save the new element attributes. * * (Some mesh modifications, that use methods on the mesh class directly, will automatically record that * the mesh has been modified. Since we're just modifying elements, this information isn't propagated at * present.) */ mesh.SetMeshHasChangedSinceLoading(); /* * The external conductivity can set two ways: * * the default conductivity in the bath is set with {{{SetBathConductivity(double)}}} * * heterogeneous overides can be set with {{{SetBathMultipleConductivities(std::map<unsigned, double> )}}} */ HeartConfig::Instance()->SetBathConductivity(7.0); //bath_id1 tags will take the default value (actually 7.0 is the default) std::map<unsigned, double> multiple_bath_conductivities; multiple_bath_conductivities[bath_id2] = 6.5; // mS/cm HeartConfig::Instance()->SetBathMultipleConductivities(multiple_bath_conductivities); /* Now we define the electrodes. First define the magnitude of the electrodes * (ie the magnitude of the boundary extracellular stimulus), and the duration * it lasts for. Currently, electrodes switch on at time 0 and have constant magnitude * until they are switched off. (Note that this test has a small range of * magnitudes that will work, perhaps because the electrodes are close to the tissue). */ // For default conductivities and explicit cell model -1e4 is under threshold, -1.4e4 too high - crashes the cell model // For heterogeneous conductivities as given, -1e4 is under threshold double magnitude = -14.0e3; // uA/cm^2 double start_time = 0.0; double duration = 1; //ms /* Electrodes work in two ways: the first electrode applies an input flux, and * the opposite electrode can either be grounded or apply an equal and opposite * flux (ie an output flux). The `false` here indicates the second electrode * is not grounded, ie has an equal and opposite flux. The "0" indicates * that the electrodes should be applied to the bounding surfaces in the x-direction * (1 would be y-direction, 2 z-direction), which are X=0.0 and X=0.1 in the given mesh. * (This explains why the full mesh ought to be rectangular/cuboid - the nodes on * x=xmin and x=xmax ought to be form two surfaces of equal area. */ HeartConfig::Instance()->SetElectrodeParameters(false, 0, magnitude, start_time, duration); /* Now create the problem class, using the cell factory and passing * in `true` as the second argument to indicate we are solving a bath * problem.. */ BidomainProblem<2> bidomain_problem( &cell_factory, true ); /* ..set the mesh and electrodes.. */ bidomain_problem.SetMesh(&mesh); /* ..and solve as before. */ bidomain_problem.Initialise(); bidomain_problem.Solve(); /* The results can be visualised as before. '''Note:''' The voltage is only * defined at cardiac nodes (a node contained in ''any'' cardiac element), but * for visualisation and computation a 'fake' value of ZERO is given for the * voltage at bath nodes. * * Finally, we can check that an AP was induced in any of the cardiac * cells. We use a `ReplicatableVector` as before, and make sure we * only check the voltage at cardiac cells. */ Vec solution = bidomain_problem.GetSolution(); // the Vs and phi_e's, as a PetSc vector ReplicatableVector solution_repl(solution); bool ap_triggered = false; for (AbstractTetrahedralMesh<2,2>::NodeIterator iter = mesh.GetNodeIteratorBegin(); iter != mesh.GetNodeIteratorEnd(); ++iter) { if (HeartRegionCode::IsRegionTissue( iter->GetRegion() )) { if (solution_repl[2*iter->GetIndex()] > 0.0) // 2*i, ie the voltage for this node (would be 2*i+1 for phi_e for this node) { ap_triggered = true; } } } TS_ASSERT(ap_triggered); }
void Test2DSimulations() throw(Exception) { double conductivity_scale = 1; double h = 0.01; // cm double ode_time_step = 0.005; //ms double pde_time_step = 0.01; //ms unsigned num_stims = 1; TetrahedralMesh<2,2> mesh; unsigned num_elem_x = (unsigned)(0.5/h); // num elements to make 5mm unsigned num_elem_y = (unsigned)(0.5/h); // num elements to make 5mm //unsigned num_elem_z = (unsigned)(0.15/h);// Num elements to make 0.3cm double pacing_cycle_length = 350; double stim_mag = -500000; double stim_dur = 3; double area = 0.005; mesh.ConstructRectangularMesh(num_elem_x, num_elem_y); mesh.Scale(h,h); // Get mesh into units of cm. std::string archive_dir_base("LongPostprocessing_archives/archive"); std::string archive_dir_current; // Setup HeartConfig::Instance()->SetSimulationDuration(pacing_cycle_length); //ms HeartConfig::Instance()->SetOutputDirectory("LongPostprocessing"); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); // These lines make postprocessing fast or slow. HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(ode_time_step, pde_time_step, 10); // Leads to 10MB VTK file //HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(ode_time_step, pde_time_step, 0.01); // Leads to 1GB VTK file HeartConfig::Instance()->SetIntracellularConductivities(Create_c_vector(1.4*conductivity_scale*1.171, 1.4*conductivity_scale*1.171)); HeartConfig::Instance()->SetSurfaceAreaToVolumeRatio(1400.0); // 1/cm HeartConfig::Instance()->SetCapacitance(1.0); // uF/cm^2 HeartConfig::Instance()->SetVisualizeWithMeshalyzer(); #ifdef CHASTE_VTK HeartConfig::Instance()->SetVisualizeWithVtk(); #endif std::vector<std::pair<double,double> > apds_requested; apds_requested.push_back(std::pair<double, double>(90,-30)); //repolarisation percentage and threshold HeartConfig::Instance()->SetApdMaps(apds_requested); // std::vector<double> excitation_threshold; // excitation_threshold.push_back(-30.0); // HeartConfig::Instance()->SetUpstrokeTimeMaps(excitation_threshold); // HeartConfig::Instance()->SetMaxUpstrokeVelocityMaps(excitation_threshold); for (unsigned stim_counter=0; stim_counter < num_stims; stim_counter++ ) { // Load problem MonodomainProblem<2> *p_monodomain_problem; if (stim_counter==0) { PointStimulusCellFactory<2> cell_factory(stim_mag, stim_dur, pacing_cycle_length, area); p_monodomain_problem = new MonodomainProblem<2>( &cell_factory ); p_monodomain_problem->SetMesh(&mesh); p_monodomain_problem->Initialise(); } else { p_monodomain_problem = CardiacSimulationArchiver<MonodomainProblem<2> >::Load(archive_dir_current); } HeartConfig::Instance()->SetSimulationDuration((double) (stim_counter+1)*pacing_cycle_length); //ms // set new directories to work from std::stringstream stringoutput; stringoutput << stim_counter; std::string stim_counter_string = stringoutput.str(); archive_dir_current = archive_dir_base + "_" + stim_counter_string; OutputFileHandler archive_directory(archive_dir_current, true); // Clean a folder for new results HeartConfig::Instance()->SetOutputFilenamePrefix("results_" + stim_counter_string); // Solve problem (this does the postprocessing too when HeartConfig options are set). p_monodomain_problem->Solve(); HeartEventHandler::Headings(); HeartEventHandler::Report(); // Save problem to archive CardiacSimulationArchiver<MonodomainProblem<2> >::Save(*p_monodomain_problem, archive_dir_current, false); std::cout << "Archived to " << archive_dir_current << "\n" << std::flush; // Copy the postprocessing results into the archive folders so they aren't wiped. std::vector<std::string> files; files.push_back("Apd_90_minus_30_Map"); // files.push_back("MaxUpstrokeVelocityMap_-30"); // files.push_back("UpstrokeTimeMap_-30"); for (unsigned i=0; i<files.size(); i++) { FileFinder file_to_copy(HeartConfig::Instance()->GetOutputDirectory() + "/output/" + files[i] + ".dat", RelativeTo::ChasteTestOutput); TS_ASSERT(file_to_copy.IsFile()); archive_directory.CopyFileTo(file_to_copy); } }// close for loop }//close void Test2dSimulations
void TestMatrixBasedAssembledBath(void) { HeartConfig::Instance()->SetSimulationDuration(1.0); //ms // need to create a cell factory but don't want any intra stim, so magnitude // of stim is zero. c_vector<double,2> centre; centre(0) = 0.05; centre(1) = 0.05; BathCellFactory<2> cell_factory( 0.0, centre); //boundary flux for Phi_e double boundary_flux = -4e2; double duration = 0.2; //ms DistributedTetrahedralMesh<2,2>* p_mesh = Load2dMeshAndSetCircularTissue<DistributedTetrahedralMesh<2,2> >( "mesh/test/data/2D_0_to_1mm_400_elements", 0.05, 0.05, 0.02); /////////////////////////////////////////////////////////////////// // matrix based /////////////////////////////////////////////////////////////////// HeartConfig::Instance()->SetOutputDirectory("BidomainBathMatrixBased"); HeartConfig::Instance()->SetOutputFilenamePrefix("matrix_based"); BidomainWithBathProblem<2> matrix_based_bido( &cell_factory ); HeartConfig::Instance()->SetElectrodeParameters(true,0,boundary_flux, 0.0, duration); { Timer::Reset(); matrix_based_bido.SetMesh(p_mesh); matrix_based_bido.Initialise(); matrix_based_bido.Solve(); Timer::Print("2D Matrix based"); } /////////////////////////////////////////////////////////////////// // non matrix based /////////////////////////////////////////////////////////////////// HeartConfig::Instance()->SetOutputDirectory("BidomainBathNonMatrixBased"); HeartConfig::Instance()->SetOutputFilenamePrefix("non_matrix_based"); BidomainWithBathProblem<2> non_matrix_based_bido( &cell_factory); { Timer::Reset(); non_matrix_based_bido.SetMesh(p_mesh); non_matrix_based_bido.Initialise(); non_matrix_based_bido.Solve(); Timer::Print("2D non matrix based"); } /////////////////////////////////////////////////////////////////// // compare /////////////////////////////////////////////////////////////////// DistributedVector matrix_based_solution = matrix_based_bido.GetSolutionDistributedVector(); DistributedVector non_matrix_based_solution = non_matrix_based_bido.GetSolutionDistributedVector(); DistributedVector::Stripe matrix_based_voltage(matrix_based_solution, 0); DistributedVector::Stripe non_matrix_based_voltage(non_matrix_based_solution, 0); DistributedVector::Stripe matrix_based_ex_pot(matrix_based_solution, 1); DistributedVector::Stripe non_matrix_based_ex_pot(non_matrix_based_solution, 1); for (DistributedVector::Iterator index = matrix_based_solution.Begin(); index != matrix_based_solution.End(); ++index) { TS_ASSERT_DELTA(matrix_based_voltage[index], non_matrix_based_voltage[index], 1e-7); //TS_ASSERT_DELTA(matrix_based_ex_pot[index], non_matrix_based_ex_pot[index], 1e-7); //std::cout << matrix_based_voltage[index] << std::endl; } delete p_mesh; }
void Test1dApd() throw(Exception) { HeartConfig::Instance()->SetPrintingTimeStep(1.0); HeartConfig::Instance()->SetSimulationDuration(400); //ms DistributedTetrahedralMesh<1,1> mesh; mesh.ConstructRegularSlabMesh(0.01, 1.0); // h=0.01cm, width=1cm PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 1> cell_factory(-600.0*1000); ////////////////////////////////////////////////////////////////////////// // run original simulation - no adaptivity, dt=0.01 all the way through ////////////////////////////////////////////////////////////////////////// HeartConfig::Instance()->SetOutputDirectory("MonoWithTimeAdaptivity1dLong/OrigNoAdapt"); MonodomainProblem<1> problem(&cell_factory); problem.SetMesh(&mesh); problem.Initialise(); problem.Solve(); HeartEventHandler::Headings(); HeartEventHandler::Report(); ////////////////////////////////////////////////////////////////////////// // run adaptive simulation - dt=0.01 for first 2ms, then dt=1 ////////////////////////////////////////////////////////////////////////// HeartConfig::Instance()->SetOutputDirectory("MonoWithTimeAdaptivity1dLong/SimpleAdapt"); MonodomainProblem<1> adaptive_problem(&cell_factory); adaptive_problem.SetMesh(&mesh); FixedTimeAdaptivityController controller(25); adaptive_problem.SetUseTimeAdaptivityController(true, &controller); adaptive_problem.Initialise(); adaptive_problem.Solve(); HeartEventHandler::Headings(); HeartEventHandler::Report(); Hdf5DataReader reader_no_adapt("MonoWithTimeAdaptivity1dLong/OrigNoAdapt","SimulationResults"); Hdf5DataReader reader_adapt("MonoWithTimeAdaptivity1dLong/SimpleAdapt","SimulationResults"); unsigned num_timesteps = reader_no_adapt.GetUnlimitedDimensionValues().size(); assert(num_timesteps == reader_adapt.GetUnlimitedDimensionValues().size()); DistributedVectorFactory factory(mesh.GetNumNodes()); Vec voltage_no_adapt = factory.CreateVec(); Vec voltage_adapt = factory.CreateVec(); Vec difference; VecDuplicate(voltage_adapt, &difference); for (unsigned timestep=0; timestep<num_timesteps; timestep++) { reader_no_adapt.GetVariableOverNodes(voltage_no_adapt, "V", timestep); reader_adapt.GetVariableOverNodes(voltage_adapt, "V", timestep); PetscVecTools::WAXPY(difference, -1.0, voltage_adapt, voltage_no_adapt); double l_inf_norm; VecNorm(difference, NORM_INFINITY, &l_inf_norm); //std::cout << l_inf_norm << "\n"; if (timestep < 25) { TS_ASSERT_DELTA(l_inf_norm, 0.0, 1e-10); // first 25 ms, there should be no difference } else { TS_ASSERT_DELTA(l_inf_norm, 0.0, 2.25); // the difference is at most ~2mv, which occurs during the downstroke } } PetscTools::Destroy(voltage_no_adapt); PetscTools::Destroy(voltage_adapt); }
void TestBidomainProblemWithDistributedMesh2D() throw(Exception) { HeartConfig::Instance()->SetSimulationDuration(1); //ms HeartConfig::Instance()->SetOutputDirectory("DistributedMesh2d"); HeartConfig::Instance()->SetOutputFilenamePrefix("tetrahedral2d"); // The default stimulus in PlaneStimulusCellFactory is not enough to generate propagation // here, increasing it an order of magnitude PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 2> cell_factory(-6000); // To avoid an issue with the Event handler only one simulation should be // in existance at a time: therefore monodomain simulation is defined in a block double seq_ave_voltage; { /////////////////////////////////////////////////////////////////// // TetrahedralMesh /////////////////////////////////////////////////////////////////// TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/2D_0_to_1mm_400_elements"); TetrahedralMesh<2,2> mesh; mesh.ConstructFromMeshReader(mesh_reader); BidomainProblem<2> nondistributed_problem( &cell_factory ); nondistributed_problem.SetMesh(&mesh); nondistributed_problem.Initialise(); HeartConfig::Instance()->SetSurfaceAreaToVolumeRatio(1.0); HeartConfig::Instance()->SetCapacitance(1.0); nondistributed_problem.Solve(); DistributedVector dist_nondistributed_voltage = nondistributed_problem.GetSolutionDistributedVector(); DistributedVector::Stripe nondistributed_voltage(dist_nondistributed_voltage, 0); DistributedVector::Stripe nondistributed_potential(dist_nondistributed_voltage, 1); double seq_local_ave_voltage = 0.0; for (DistributedVector::Iterator index = dist_nondistributed_voltage.Begin(); index != dist_nondistributed_voltage.End(); ++index) { if (index.Global==0) { TS_ASSERT_LESS_THAN(0, nondistributed_voltage[index]); } seq_local_ave_voltage += nondistributed_voltage[index]; } MPI_Reduce(&seq_local_ave_voltage, &seq_ave_voltage, 1, MPI_DOUBLE, MPI_SUM, PetscTools::MASTER_RANK, PETSC_COMM_WORLD); seq_ave_voltage /= mesh.GetNumNodes(); } /////////////////////////////////////////////////////////////////// // DistributedTetrahedralMesh /////////////////////////////////////////////////////////////////// HeartConfig::Instance()->SetOutputFilenamePrefix("distributed2d"); TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/2D_0_to_1mm_400_elements"); DistributedTetrahedralMesh<2,2> mesh(DistributedTetrahedralMeshPartitionType::DUMB); mesh.ConstructFromMeshReader(mesh_reader); BidomainProblem<2> distributed_problem( &cell_factory ); distributed_problem.SetMesh(&mesh); distributed_problem.Initialise(); HeartConfig::Instance()->SetSurfaceAreaToVolumeRatio(1.0); HeartConfig::Instance()->SetCapacitance(1.0); distributed_problem.Solve(); DistributedVector dist_distributed_voltage = distributed_problem.GetSolutionDistributedVector(); DistributedVector::Stripe distributed_voltage(dist_distributed_voltage, 0); DistributedVector::Stripe distributed_potential(dist_distributed_voltage, 1); double para_local_ave_voltage = 0.0; for (DistributedVector::Iterator index = dist_distributed_voltage.Begin(); index != dist_distributed_voltage.End(); ++index) { if (index.Global==0) { TS_ASSERT_LESS_THAN(0, distributed_voltage[index]); } para_local_ave_voltage += distributed_voltage[index]; } double para_ave_voltage; MPI_Reduce(¶_local_ave_voltage, ¶_ave_voltage, 1, MPI_DOUBLE, MPI_SUM, PetscTools::MASTER_RANK, PETSC_COMM_WORLD); para_ave_voltage /= mesh.GetNumNodes(); /////////////////////////////////////////////////////////////////// // compare /////////////////////////////////////////////////////////////////// if (PetscTools::AmMaster()) { std::cout << seq_ave_voltage << " " << para_ave_voltage << std::endl; TS_ASSERT_DELTA(seq_ave_voltage, para_ave_voltage, 1.0); } }