void TestConstructStreeterOnRightWedge() throw(Exception) { TrianglesMeshReader<3,3> mesh_reader("heart/test/data/human_wedge_mesh/HumanWedgeMesh"); std::string epi_face_file = "heart/test/data/human_wedge_mesh/epi.tri"; std::string endo_face_file = "heart/test/data/human_wedge_mesh/endo.tri"; DistributedTetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); StreeterFibreGenerator<3> fibre_generator(mesh); //Assume we are in the left ventricle fibre_generator.SetSurfaceFiles(epi_face_file, endo_face_file, "", true); fibre_generator.SetApexToBase(0); fibre_generator.SetWriteFileAsBinary(); OutputFileHandler handler("human_wedge_mesh", false); fibre_generator.WriteData(handler, "HumanWedgeMeshRight.ortho"); FileFinder fibre_file1 = handler.FindFile("HumanWedgeMeshRight.ortho"); FileFinder fibre_file2("heart/test/data/human_wedge_mesh/HumanWedgeMeshRight.ortho", RelativeTo::ChasteSourceRoot); CompareGeneratedWithReferenceFile(fibre_file1, ORTHO, fibre_file2, ORTHO); }
void TestSetLogInfo() throw (Exception) { TrianglesMeshReader<3,3> mesh_reader("heart/test/data/box_shaped_heart/box_heart"); std::string epi_face_file = "heart/test/data/box_shaped_heart/epi.tri"; std::string rv_face_file = "heart/test/data/box_shaped_heart/rv.tri"; std::string lv_face_file = "heart/test/data/box_shaped_heart/lv.tri"; DistributedTetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); StreeterFibreGenerator<3> fibre_generator(mesh); fibre_generator.SetSurfaceFiles(epi_face_file, rv_face_file, lv_face_file, false); fibre_generator.SetApexToBase(0); OutputFileHandler handler("shorter_streeter_loginfo"); fibre_generator.WriteData(handler, "box_heart.ortho"); FileFinder node_regions_file = handler.FindFile("node_regions.data"); FileFinder wall_thickness_file = handler.FindFile("wall_thickness.data"); FileFinder averaged_thickness_file = handler.FindFile("averaged_thickness.data"); TS_ASSERT_EQUALS(node_regions_file.IsFile(), false); TS_ASSERT_EQUALS(wall_thickness_file.IsFile(), false); TS_ASSERT_EQUALS(averaged_thickness_file.IsFile(), false); fibre_generator.SetLogInfo(true); fibre_generator.WriteData(handler, "box_heart.ortho"); TS_ASSERT_EQUALS(node_regions_file.IsFile(), true); TS_ASSERT_EQUALS(wall_thickness_file.IsFile(), true); TS_ASSERT_EQUALS(averaged_thickness_file.IsFile(), true); }
void TestSimpleOrthotropic() throw (Exception) { TrianglesMeshReader<3,3> mesh_reader("heart/test/data/box_shaped_heart/box_heart"); std::string epi_face_file = "heart/test/data/box_shaped_heart/epi.tri"; std::string rv_face_file = "heart/test/data/box_shaped_heart/rv.tri"; std::string lv_face_file = "heart/test/data/box_shaped_heart/lv.tri"; DistributedTetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); StreeterFibreGenerator<3> fibre_generator(mesh); fibre_generator.SetSurfaceFiles(epi_face_file, rv_face_file, lv_face_file, false); fibre_generator.SetApexToBase(0); OutputFileHandler handler("shorter_streeter", false); fibre_generator.WriteData(handler, "box_heart.ortho"); FileFinder fibre_file_ascii = handler.FindFile("box_heart.ortho"); FileFinder fibre_file_reference("heart/test/data/box_shaped_heart/box_heart.ortho", RelativeTo::ChasteSourceRoot); CompareGeneratedWithReferenceFile(fibre_file_ascii, ORTHO, fibre_file_reference, ORTHO); fibre_generator.SetWriteFileAsBinary(); fibre_generator.WriteData(handler, "box_heart_binary.ortho"); FileFinder fibre_file_binary = handler.FindFile("box_heart_binary.ortho"); CompareGeneratedWithReferenceFile(fibre_file_binary, ORTHO, fibre_file_reference, ORTHO); }
void TestCheckForBathElementsNoDeadlock() throw (Exception) { HeartConfig::Instance()->SetSimulationDuration(1.0); //ms HeartConfig::Instance()->SetOutputDirectory("bidomain_bath"); HeartConfig::Instance()->SetOutputFilenamePrefix("BidomainLR91_1d"); PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 1> bidomain_cell_factory; BidomainWithBathProblem<1> bidomain_problem( &bidomain_cell_factory ); TrianglesMeshReader<1,1> reader("mesh/test/data/1D_0_to_1_100_elements"); DistributedTetrahedralMesh<1,1> mesh; mesh.ConstructFromMeshReader(reader); try { mesh.GetElement(0)->SetAttribute(HeartRegionCode::GetValidBathId()); } catch(Exception&) { // I don't own element 0 } bidomain_problem.SetMesh(&mesh); // Fails because no bath TS_ASSERT_THROWS_NOTHING(bidomain_problem.Initialise()); // Prevent an EventHandling exception in later tests HeartEventHandler::EndEvent(HeartEventHandler::EVERYTHING); }
void failsInParallelTestDistributedRigidBodyMethods() { TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); DistributedTetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); c_matrix<double, 3, 3> dummy; double jacobian_det = 0.0; double scaled_volume = 0.0; for (AbstractTetrahedralMesh<3, 3>::ElementIterator iter = mesh.GetElementIteratorBegin(); iter != mesh.GetElementIteratorEnd(); ++iter) { iter->CalculateJacobian(dummy, jacobian_det); scaled_volume += jacobian_det; } TS_ASSERT_DELTA(scaled_volume, 1.0*6, 1e-6); mesh.RotateX(M_PI); //mesh.Translate(100.0, 0.0, 0.0); double scaled_volume_after = 0.0; for (AbstractTetrahedralMesh<3, 3>::ElementIterator iter = mesh.GetElementIteratorBegin(); iter != mesh.GetElementIteratorEnd(); ++iter) { iter->CalculateJacobian(dummy, jacobian_det); scaled_volume_after += jacobian_det; } TS_ASSERT_DELTA(scaled_volume_after, 1.0*6, 1e-6); }
/* * This is the same as TestConductionVelocityConvergesFasterWithSvi1d with i=2, but solves in two parts. * If that test changes, check the hardcoded values here! */ void TestArchiving() { std::string archive_dir = "monodomain_svi_archive"; std::string archive_file = "monodomain_svi.arch"; std::string output_dir = "monodomain_svi_output"; { // Save HeartConfig::Instance()->SetSimulationDuration(0.1); //ms HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(0.01, 0.01, 0.01); HeartConfig::Instance()->SetOutputDirectory(output_dir); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetUseStateVariableInterpolation(); DistributedTetrahedralMesh<1,1> mesh; mesh.ConstructRegularSlabMesh(0.02, 1.0); BlockCellFactory<1> cell_factory; MonodomainProblem<1> monodomain_problem( &cell_factory ); monodomain_problem.SetMesh(&mesh); monodomain_problem.Initialise(); monodomain_problem.Solve(); CardiacSimulationArchiver<MonodomainProblem<1> >::Save(monodomain_problem, archive_dir); } HeartConfig::Instance()->Reset(); { // Load HeartConfig::Instance()->SetUseStateVariableInterpolation(false); // Just in case... MonodomainProblem<1> *p_monodomain_problem = CardiacSimulationArchiver<MonodomainProblem<1> >::Load(archive_dir); HeartConfig::Instance()->SetSimulationDuration(4.0); //ms p_monodomain_problem->Solve(); ReplicatableVector final_voltage; final_voltage.ReplicatePetscVector(p_monodomain_problem->GetSolution()); double probe_voltage = final_voltage[15]; TS_ASSERT_DELTA(probe_voltage, 17.3131, 1e-3); delete p_monodomain_problem; } }
void TestExceptions() { TrianglesMeshReader<3,3> mesh_reader("heart/test/data/box_shaped_heart/box_heart"); DistributedTetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); StreeterFibreGenerator<3> fibre_generator(mesh); // No surfaces defined OutputFileHandler handler("streeter", false); TS_ASSERT_THROWS_THIS(fibre_generator.WriteData(handler, "file.fibres"), "Files defining the heart surfaces not set"); // Wrong surface filename TS_ASSERT_THROWS_THIS(fibre_generator.SetSurfaceFiles("wrong_name", "wrong_name", "wrong_name", false), "Wrong surface definition file name wrong_name"); std::string epi_face_file = "heart/test/data/box_shaped_heart/epi.tri"; std::string rv_face_file = "heart/test/data/box_shaped_heart/rv.tri"; std::string lv_face_file = "heart/test/data/box_shaped_heart/lv.tri"; fibre_generator.SetSurfaceFiles(epi_face_file, rv_face_file, lv_face_file, false); OutputFileHandler shorter_handler("shorter_streeter", false); TS_ASSERT_THROWS_THIS(fibre_generator.WriteData(shorter_handler, "vector_not_set.ortho"), "Apex to base vector has not been set"); TS_ASSERT_THROWS_THIS(fibre_generator.SetApexToBase(999), "Apex to base coordinate axis was out of range"); c_vector<double, 3> axis; axis[0] = 0.0; axis[1] = 0.0; axis[2] = 0.0; TS_ASSERT_THROWS_THIS(fibre_generator.SetApexToBase(axis), "Apex to base vector should be non-zero"); axis[1] = 42.0; //Will be normalised fibre_generator.SetApexToBase(axis); }
void TestNodeExchange() throw(Exception) { HeartConfig::Instance()->Reset(); HeartConfig::Instance()->Reset(); DistributedTetrahedralMesh<1,1> mesh; mesh.ConstructRegularSlabMesh(0.1, 1.0); // [0,1] with h=0.1, ie 11 node mesh MyCardiacCellFactory cell_factory; cell_factory.SetMesh(&mesh); MonodomainTissue<1> monodomain_tissue( &cell_factory, true ); if ( PetscTools::GetNumProcs() == 1 ) { TS_ASSERT_EQUALS( mesh.GetNumHaloNodes(), 0u ); } else { if ( PetscTools::AmMaster() || PetscTools::AmTopMost() ) { TS_ASSERT_EQUALS( mesh.GetNumHaloNodes(), 1u ); } else { TS_ASSERT_EQUALS( mesh.GetNumHaloNodes(), 2u ); } } for (DistributedTetrahedralMesh<1,1>::HaloNodeIterator it=mesh.GetHaloNodeIteratorBegin(); it != mesh.GetHaloNodeIteratorEnd(); ++it) { AbstractCardiacCellInterface* cell = monodomain_tissue.GetCardiacCellOrHaloCell( (*it)->GetIndex() ); TS_ASSERT_DELTA(cell->GetStimulus(0.001),0,1e-10); } if ( PetscTools::AmMaster() ) { // Master owns node 0 AbstractCardiacCellInterface* cell = monodomain_tissue.GetCardiacCellOrHaloCell(0); TS_ASSERT_DELTA(cell->GetStimulus(0.001), -80.0, 1e-10); } else { // Zero is not halo owned by any process (unless we have a lot of them). TS_ASSERT_THROWS_CONTAINS(monodomain_tissue.GetCardiacCellOrHaloCell(0), "Requested node/halo 0 does not belong to processor "); } }
void TestConductionVelocityConvergesFasterWithSvi1d() throw(Exception) { double h[3] = {0.001,0.01,0.02}; std::vector<double> conduction_vel_nci(3); std::vector<double> conduction_vel_svi(3); ReplicatableVector final_solution_ici; ReplicatableVector final_solution_svi; ReplicatableVector final_solution_svit; HeartConfig::Instance()->SetSimulationDuration(4.0); //ms HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(0.01, 0.01, 0.01); for(unsigned i=0; i<3; i++) { // ICI - ionic current interpolation - the default { TetrahedralMesh<1,1> mesh; mesh.ConstructRegularSlabMesh(h[i], 1.0); std::stringstream output_dir; output_dir << "BidomainIci_" << h[i]; HeartConfig::Instance()->SetOutputDirectory(output_dir.str()); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); // need to have this for i=1,2 cases!! HeartConfig::Instance()->SetUseStateVariableInterpolation(false); BlockCellFactory<1> cell_factory; BidomainProblem<1> bidomain_problem( &cell_factory ); bidomain_problem.SetMesh(&mesh); bidomain_problem.Initialise(); bidomain_problem.Solve(); final_solution_ici.ReplicatePetscVector(bidomain_problem.GetSolution()); } // SVI - state variable interpolation { DistributedTetrahedralMesh<1,1> mesh; mesh.ConstructRegularSlabMesh(h[i], 1.0); std::stringstream output_dir; output_dir << "BidomainSvi_" << h[i]; HeartConfig::Instance()->SetOutputDirectory(output_dir.str()); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetUseStateVariableInterpolation(); BlockCellFactory<1> cell_factory; BidomainProblem<1> bidomain_problem( &cell_factory ); bidomain_problem.SetMesh(&mesh); bidomain_problem.Initialise(); bidomain_problem.Solve(); final_solution_svi.ReplicatePetscVector(bidomain_problem.GetSolution()); } // SVIT - state variable interpolation on straight (not distributed) tetrahedral mesh { TetrahedralMesh<1,1> mesh; mesh.ConstructRegularSlabMesh(h[i], 1.0); std::stringstream output_dir; output_dir << "BidomainSviTet_" << h[i]; HeartConfig::Instance()->SetOutputDirectory(output_dir.str()); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetUseStateVariableInterpolation(); BlockCellFactory<1> cell_factory; BidomainProblem<1> bidomain_problem( &cell_factory ); bidomain_problem.SetMesh(&mesh); bidomain_problem.Initialise(); bidomain_problem.Solve(); final_solution_svit.ReplicatePetscVector(bidomain_problem.GetSolution()); } double voltage_at_0_03_finest_mesh; if(i==0) // finest mesh { for(unsigned j=0; j<final_solution_ici.GetSize(); j++) { // visually checked they agree at this mesh resolution, and chosen tolerance from results TS_ASSERT_DELTA(final_solution_ici[j], final_solution_svi[j], 0.35); TS_ASSERT_DELTA(final_solution_svit[j], final_solution_svi[j], 1e-8); if(j%2==0 /* just look at voltage */ && final_solution_ici[j]>-80) { // shouldn't be exactly equal, as long as away from resting potential TS_ASSERT_DIFFERS(final_solution_ici[j], final_solution_svi[j]); } } voltage_at_0_03_finest_mesh = final_solution_ici[600]; TS_ASSERT_DELTA(voltage_at_0_03_finest_mesh, -65.2218, 1e-2); //hardcoded value } else if(i==1) { double nci_voltage_at_0_03_middle_mesh = final_solution_ici[60]; double svi_voltage_at_0_03_middle_mesh = final_solution_svi[60]; double svit_voltage_at_0_03_middle_mesh = final_solution_svit[60]; // ICI conduction velocity > SVI conduction velocity // and both should be greater than CV on finesh mesh TS_ASSERT_DELTA(nci_voltage_at_0_03_middle_mesh, -44.3111, 1e-3); TS_ASSERT_DELTA(svi_voltage_at_0_03_middle_mesh, -60.7765, 1e-3); TS_ASSERT_DELTA(svit_voltage_at_0_03_middle_mesh, -60.7765, 1e-3); } else { double nci_voltage_at_0_03_coarse_mesh = final_solution_ici[30]; double svi_voltage_at_0_03_coarse_mesh = final_solution_svi[30]; double svit_voltage_at_0_03_coarse_mesh = final_solution_svit[30]; // ICI conduction velocity even greater than SVI conduction // velocity TS_ASSERT_DELTA(nci_voltage_at_0_03_coarse_mesh, -6.5622, 1e-3); TS_ASSERT_DELTA(svi_voltage_at_0_03_coarse_mesh, -51.8848, 1e-3); TS_ASSERT_DELTA(svit_voltage_at_0_03_coarse_mesh, -51.8848, 1e-3); } } }
void TestConductionVelocityConvergesFasterWithSvi1d() { double h[3] = {0.001,0.01,0.02}; unsigned probe_node_index[3] = {300, 30, 15}; unsigned number_of_nodes[3] = {1001, 101, 51}; std::vector<double> conduction_vel_ici(3); std::vector<double> conduction_vel_svi(3); ReplicatableVector final_voltage_ici; ReplicatableVector final_voltage_svi; //HeartConfig::Instance()->SetUseRelativeTolerance(1e-8); HeartConfig::Instance()->SetSimulationDuration(4.0); //ms HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(0.01, 0.01, 0.01); for (unsigned i=0; i<3; i++) { // ICI - ionic current interpolation - the default { DistributedTetrahedralMesh<1,1> mesh; mesh.ConstructRegularSlabMesh(h[i], 1.0); TS_ASSERT_EQUALS(mesh.GetNumNodes(), number_of_nodes[i]); //Double check (for later) that the indexing is as expected if (mesh.GetDistributedVectorFactory()->IsGlobalIndexLocal( probe_node_index[i] )) { TS_ASSERT_DELTA(mesh.GetNode( probe_node_index[i] )->rGetLocation()[0], 0.3, 1e-8); } std::stringstream output_dir; output_dir << "MonodomainIci_" << h[i]; HeartConfig::Instance()->SetOutputDirectory(output_dir.str()); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); // need to have this for i=1,2 cases!! HeartConfig::Instance()->SetUseStateVariableInterpolation(false); BlockCellFactory<1> cell_factory; MonodomainProblem<1> monodomain_problem( &cell_factory ); monodomain_problem.SetMesh(&mesh); monodomain_problem.Initialise(); monodomain_problem.Solve(); final_voltage_ici.ReplicatePetscVector(monodomain_problem.GetSolution()); //// see #1633 //// end time needs to be increased for these (say, to 7ms) // Hdf5DataReader simulation_data(OutputFileHandler::GetChasteTestOutputDirectory() + output_dir.str(), // "results", false); // PropagationPropertiesCalculator ppc(&simulation_data); // unsigned node_at_0_04 = (unsigned)round(0.04/h[i]); // unsigned node_at_0_40 = (unsigned)round(0.40/h[i]); // assert(fabs(mesh.GetNode(node_at_0_04)->rGetLocation()[0]-0.04)<1e-6); // assert(fabs(mesh.GetNode(node_at_0_40)->rGetLocation()[0]-0.40)<1e-6); // conduction_vel_ici[i] = ppc.CalculateConductionVelocity(node_at_0_04,node_at_0_40,0.36); // std::cout << "conduction_vel_ici = " << conduction_vel_ici[i] << "\n"; } // SVI - state variable interpolation { DistributedTetrahedralMesh<1,1> mesh; mesh.ConstructRegularSlabMesh(h[i], 1.0); //Double check (for later) that the indexing is as expected if (mesh.GetDistributedVectorFactory()->IsGlobalIndexLocal( probe_node_index[i] )) { TS_ASSERT_DELTA(mesh.GetNode( probe_node_index[i] )->rGetLocation()[0], 0.3, 1e-8); } std::stringstream output_dir; output_dir << "MonodomainSvi_" << h[i]; HeartConfig::Instance()->SetOutputDirectory(output_dir.str()); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetUseStateVariableInterpolation(); BlockCellFactory<1> cell_factory; MonodomainProblem<1> monodomain_problem( &cell_factory ); monodomain_problem.SetMesh(&mesh); monodomain_problem.Initialise(); monodomain_problem.Solve(); final_voltage_svi.ReplicatePetscVector(monodomain_problem.GetSolution()); // Hdf5DataReader simulation_data(OutputFileHandler::GetChasteTestOutputDirectory() + output_dir.str(), // "results", false); // PropagationPropertiesCalculator ppc(&simulation_data); // unsigned node_at_0_04 = (unsigned)round(0.04/h[i]); // unsigned node_at_0_40 = (unsigned)round(0.40/h[i]); // assert(fabs(mesh.GetNode(node_at_0_04)->rGetLocation()[0]-0.04)<1e-6); // assert(fabs(mesh.GetNode(node_at_0_40)->rGetLocation()[0]-0.40)<1e-6); // conduction_vel_svi[i] = ppc.CalculateConductionVelocity(node_at_0_04,node_at_0_40,0.36); // std::cout << "conduction_vel_svi = " << conduction_vel_svi[i] << "\n"; } if (i==0) // finest mesh { for (unsigned j=0; j<final_voltage_ici.GetSize(); j++) { // visually checked they agree at this mesh resolution, and chosen tolerance from results TS_ASSERT_DELTA(final_voltage_ici[j], final_voltage_svi[j], 0.3); if (final_voltage_ici[j]>-80) { // shouldn't be exactly equal, as long as away from resting potential TS_ASSERT_DIFFERS(final_voltage_ici[j], final_voltage_svi[j]); } } double ici_voltage_at_0_03_finest_mesh = final_voltage_ici[ probe_node_index[i] ]; double svi_voltage_at_0_03_finest_mesh = final_voltage_svi[ probe_node_index[i] ]; TS_ASSERT_DELTA(svi_voltage_at_0_03_finest_mesh, 11.0067, 2e-4); //hardcoded value from fine svi TS_ASSERT_DELTA(ici_voltage_at_0_03_finest_mesh, 11.0067, 1.2e-1); //hardcoded value from fine svi } else if (i==1) { double ici_voltage_at_0_03_middle_mesh = final_voltage_ici[ probe_node_index[i] ]; double svi_voltage_at_0_03_middle_mesh = final_voltage_svi[ probe_node_index[i] ]; // ICI conduction velocity > SVI conduction velocity // and both should be greater than CV on finesh mesh TS_ASSERT_DELTA(ici_voltage_at_0_03_middle_mesh, 19.8924, 1e-3); TS_ASSERT_DELTA(svi_voltage_at_0_03_middle_mesh, 14.9579, 1e-3); } else { double ici_voltage_at_0_03_coarse_mesh = final_voltage_ici[ probe_node_index[i] ]; double svi_voltage_at_0_03_coarse_mesh = final_voltage_svi[ probe_node_index[i] ]; // ICI conduction velocity even greater than SVI conduction // velocity TS_ASSERT_DELTA(ici_voltage_at_0_03_coarse_mesh, 24.4938, 1e-3); TS_ASSERT_DELTA(svi_voltage_at_0_03_coarse_mesh, 17.3131, 1e-3); } } }
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); }
/** * Tests archiving of the tissue object. * It creates one, changes the default values of some member variables and saves. * Then it tries to load from the archive and checks that the member variables are with the right values. */ void TestSaveAndLoadExtendedBidomainTissue() throw (Exception) { HeartConfig::Instance()->Reset(); // Archive settings FileFinder archive_dir("extended_tissue_archive", RelativeTo::ChasteTestOutput); std::string archive_file = "extended_bidomain_tissue.arch"; bool cache_replication_saved = false; double saved_printing_timestep = 2.0; double default_printing_timestep = HeartConfig::Instance()->GetPrintingTimeStep(); c_matrix<double, 3, 3> intra_tensor_before_archiving; c_matrix<double, 3, 3> intra_tensor_second_cell_before_archiving; c_matrix<double, 3, 3> extra_tensor_before_archiving; //creation and save { // This call is required to set the appropriate conductivity media and to make sure that HeartConfig // knows the mesh filename despite we use our own mesh reader. HeartConfig::Instance()->SetMeshFileName("mesh/test/data/cube_136_elements"); TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); DistributedTetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); UnStimulatedCellFactory first_cell; StimulatedCellFactory second_cell; ExtracellularStimulusFactory extra_factory; first_cell.SetMesh(&mesh); second_cell.SetMesh(&mesh); extra_factory.SetMesh(&mesh); ExtendedBidomainTissue<3> extended_tissue( &first_cell, &second_cell , &extra_factory); //set a value different from default for the conductivities of the second cell extended_tissue.SetIntracellularConductivitiesSecondCell(Create_c_vector(25.0,26.0,27.0)); //this is normally done by the problem class, but in this test we do it manually extended_tissue.CreateIntracellularConductivityTensorSecondCell(); extended_tissue.SetCacheReplication(cache_replication_saved); // Not the default to check it is archived... //shuffle default values to check if they get archived properly extended_tissue.SetAmFirstCell(11.0); extended_tissue.SetAmSecondCell(22.0); extended_tissue.SetAmGap(33.0); extended_tissue.SetCmFirstCell(44.0); extended_tissue.SetCmSecondCell(55.0); extended_tissue.SetGGap(66.0); //again, away from default value to check for archiving extended_tissue.SetUserSuppliedExtracellularStimulus(true); //set some heterogeneities in Ggap std::vector<boost::shared_ptr<AbstractChasteRegion<3> > > heterogeneity_areas; std::vector<double> Ggap_values; ChastePoint<3> cornerA(-1, -1, 0); ChastePoint<3> cornerB(0.001, 0.001, 0.001); boost::shared_ptr<ChasteCuboid<3> > p_cuboid_1(new ChasteCuboid<3>(cornerA, cornerB)); heterogeneity_areas.push_back(p_cuboid_1); //within the first area Ggap_values.push_back(143.0); extended_tissue.SetGgapHeterogeneities(heterogeneity_areas, Ggap_values); extended_tissue.CreateGGapConductivities(); // Some checks to make sure HeartConfig is being saved and loaded by this too. HeartConfig::Instance()->SetPrintingTimeStep(saved_printing_timestep); TS_ASSERT_DELTA(HeartConfig::Instance()->GetPrintingTimeStep(), saved_printing_timestep, 1e-9); intra_tensor_before_archiving = extended_tissue.rGetIntracellularConductivityTensor(0); intra_tensor_second_cell_before_archiving = extended_tissue.rGetIntracellularConductivityTensorSecondCell(0); extra_tensor_before_archiving = extended_tissue.rGetExtracellularConductivityTensor(0); // Save ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); AbstractCardiacTissue<3>* const p_archive_bidomain_tissue = &extended_tissue; (*p_arch) << p_archive_bidomain_tissue; HeartConfig::Reset(); TS_ASSERT_DELTA(HeartConfig::Instance()->GetPrintingTimeStep(), default_printing_timestep, 1e-9); TS_ASSERT_DIFFERS(saved_printing_timestep, default_printing_timestep); } //load { ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); AbstractCardiacTissue<3>* p_abstract_tissue; (*p_arch) >> p_abstract_tissue; assert(p_abstract_tissue!=NULL); //dynamic cast so we are able to test specific variables of ExtendedBidomainTissue ExtendedBidomainTissue<3>* p_extended_tissue = dynamic_cast<ExtendedBidomainTissue<3>*>(p_abstract_tissue); assert(p_extended_tissue != NULL); const c_matrix<double, 3, 3>& intra_tensor_after_archiving = p_extended_tissue->rGetIntracellularConductivityTensor(0); const c_matrix<double, 3, 3>& intra_tensor_second_cell_after_archiving = p_extended_tissue->rGetIntracellularConductivityTensorSecondCell(0); const c_matrix<double, 3, 3>& extra_tensor_after_archiving = p_extended_tissue->rGetExtracellularConductivityTensor(0); //check before archiving = after archiving for(unsigned i=0; i<3; i++) { for(unsigned j=0; j<3; j++) { TS_ASSERT_DELTA(intra_tensor_before_archiving(i,j), intra_tensor_after_archiving(i,j), 1e-9); TS_ASSERT_DELTA(intra_tensor_second_cell_before_archiving(i,j), intra_tensor_second_cell_after_archiving(i,j), 1e-9); TS_ASSERT_DELTA(extra_tensor_before_archiving(i,j), extra_tensor_after_archiving(i,j), 1e-9); } } //check that the member variable mIntracellularConductivitiesSecondCell was archived properly TS_ASSERT_EQUALS(p_extended_tissue->GetIntracellularConductivitiesSecondCell()(0),25.0); TS_ASSERT_EQUALS(p_extended_tissue->GetIntracellularConductivitiesSecondCell()(1),26.0); TS_ASSERT_EQUALS(p_extended_tissue->GetIntracellularConductivitiesSecondCell()(2),27.0); //check that we get the same values from the archive which are different from the default TS_ASSERT_EQUALS(p_extended_tissue->GetAmFirstCell(), 11.0); TS_ASSERT_EQUALS(p_extended_tissue->GetAmSecondCell(), 22.0); TS_ASSERT_EQUALS(p_extended_tissue->GetAmGap(), 33.0); TS_ASSERT_EQUALS(p_extended_tissue->GetCmFirstCell(), 44.0); TS_ASSERT_EQUALS(p_extended_tissue->GetCmSecondCell(), 55.0); TS_ASSERT_EQUALS(p_extended_tissue->GetGGap(), 66.0); // We shouldn't need to re-build the mesh, but we use it to check that the new tissue has the same mesh // Also, when testing in parallel, we use it to get the vector factory to loop over the nodes we own. // this is because p_extended_tissue->pGetMesh()->GetDistributedVectorFactory() doesn't compile (discards qualifier stuff caused by use of const). TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/cube_136_elements"); DistributedTetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); TS_ASSERT_EQUALS(mesh.GetNumNodes(), p_extended_tissue->pGetMesh()->GetNumNodes());//note: this is allowed because GetNumNodes has const in the signature //check archiving of stimulus for first cell at some random times (it is unstimulated everywhere at all times) for (unsigned i = 0; i < mesh.GetNumNodes(); i++) { if (mesh.GetDistributedVectorFactory()->IsGlobalIndexLocal(i)) { TS_ASSERT_EQUALS(p_extended_tissue->GetCardiacCell(i)->GetIntracellularStimulus(0.0), 0.0); TS_ASSERT_EQUALS(p_extended_tissue->GetCardiacCell(i)->GetIntracellularStimulus(0.1), 0.0); TS_ASSERT_EQUALS(p_extended_tissue->GetCardiacCell(i)->GetIntracellularStimulus(2.5), 0.0); TS_ASSERT_EQUALS(p_extended_tissue->GetCardiacCell(i)->GetIntracellularStimulus(28.9), 0.0); } } //for second cell and other stuff, we probe nodes 0 and 1. unsigned node_0 = 0u; unsigned node_1 = 1u; //If the test is run in parallel, we need to work out the new indices const std::vector<unsigned>& r_permutation = mesh.rGetNodePermutation(); if (!r_permutation.empty()) { node_0 = r_permutation[0u]; node_1 = r_permutation[1u]; } //second cell is stimulated in the corner (node 0) from time 0 to 1. check it gets all this after loading if (mesh.GetDistributedVectorFactory()->IsGlobalIndexLocal(node_0)) { TS_ASSERT_EQUALS(p_extended_tissue->GetCardiacSecondCell(node_0)->GetIntracellularStimulus(0.5), -105.0*1400); TS_ASSERT_EQUALS(p_extended_tissue->GetCardiacSecondCell(node_0)->GetIntracellularStimulus(2.5), 0.0); //find local index of (the new) node_0, it should be in the heterogeneity region unsigned ownership_range_low = mesh.GetDistributedVectorFactory()->GetLow(); unsigned local_index = node_0 - ownership_range_low; //std::cout<<local_index<<std::endl; TS_ASSERT_EQUALS(p_extended_tissue->rGetGapsDistributed()[local_index],143.0);//g_gap value inside heterogeneity region } //node 0 has extracellular stimulus (1 ms from 0.1) if (mesh.GetDistributedVectorFactory()->IsGlobalIndexLocal(node_0)) { TS_ASSERT_EQUALS(p_extended_tissue->GetExtracellularStimulus(node_0)->GetStimulus(0.0), 0); TS_ASSERT_EQUALS(p_extended_tissue->GetExtracellularStimulus(node_0)->GetStimulus(0.5), -428000); TS_ASSERT_EQUALS(p_extended_tissue->GetExtracellularStimulus(node_0)->GetStimulus(1.0), -428000); TS_ASSERT_EQUALS(p_extended_tissue->GetExtracellularStimulus(node_0)->GetStimulus(1.15), 0); } //node 1 doesn't if (mesh.GetDistributedVectorFactory()->IsGlobalIndexLocal(node_1)) { TS_ASSERT_EQUALS(p_extended_tissue->GetExtracellularStimulus(node_1)->GetStimulus(0.0), 0); TS_ASSERT_EQUALS(p_extended_tissue->GetExtracellularStimulus(node_1)->GetStimulus(0.5), 0); TS_ASSERT_EQUALS(p_extended_tissue->GetExtracellularStimulus(node_1)->GetStimulus(1.0), 0); TS_ASSERT_EQUALS(p_extended_tissue->GetExtracellularStimulus(node_1)->GetStimulus(1.15), 0); //find local index of (the new) node_1, it should NOT be in the heterogeneity region unsigned ownership_range_low = mesh.GetDistributedVectorFactory()->GetLow(); unsigned local_index = node_1 - ownership_range_low; TS_ASSERT_EQUALS(p_extended_tissue->rGetGapsDistributed()[local_index],66.0);//standard g_gap value, outside heterogeneity region } //check the archiving of the flag (it would be false by default, but we set it to true before archiving) TS_ASSERT_EQUALS(p_extended_tissue->HasTheUserSuppliedExtracellularStimulus(),true); TS_ASSERT_EQUALS(cache_replication_saved, p_extended_tissue->GetDoCacheReplication()); TS_ASSERT_DELTA(HeartConfig::Instance()->GetPrintingTimeStep(), saved_printing_timestep, 1e-9); TS_ASSERT_DIFFERS(saved_printing_timestep, default_printing_timestep); // Test we are testing something in case default changes delete p_extended_tissue; } }
void TestConductivityModifier() throw(Exception) { /* * Generate a mesh. */ DistributedTetrahedralMesh<2,2> mesh; mesh.ConstructRegularSlabMesh(0.5, 1.0, 0.5); // Mesh has 4 elements /* * Here we're using a trivial cell factory for simplicity, but usually you'll provide your own one. * Set up the problem with the factory as usual. */ ZeroStimulusCellFactory<CellLuoRudy1991FromCellML,2> cell_factory; BidomainProblem<2> bidomain_problem( &cell_factory ); bidomain_problem.SetMesh( &mesh ); /* * We need to apply the modifier directly to the tissue, which comes from the problem, but is only * accessible after `Initialise()`, so let's do that now. */ bidomain_problem.Initialise(); BidomainTissue<2>* p_bidomain_tissue = bidomain_problem.GetBidomainTissue(); /* * Get the original conductivity tensor values. We haven't set them using * `HeartConfig->SetIntra/ExtracellularConductivities` so they'll just be the defaults. * * The first argument below is the element ID (we just check the first element we own here). The second accesses * the diagonal elements. We just do (0,0), as (1,1) should be the same (no fibre orientation). * Off-diagonal elements will be 0. * * As we don't have many elements, when we run on more than two processors some processors * will not own any elements. We only try to access the conductivity tensors if the process * owns at least one element. * * We then check that we have the correct (default) conductivity values. */ double orig_intra_conductivity_0 = 0.0; double orig_extra_conductivity_0 = 0.0; if (mesh.GetElementIteratorBegin() != mesh.GetElementIteratorEnd()) { unsigned first_element = mesh.GetElementIteratorBegin()->GetIndex(); orig_intra_conductivity_0 = p_bidomain_tissue->rGetIntracellularConductivityTensor(first_element)(0,0); orig_extra_conductivity_0 = p_bidomain_tissue->rGetExtracellularConductivityTensor(first_element)(0,0); TS_ASSERT_DELTA(orig_intra_conductivity_0, 1.75, 1e-9); // hard-coded using default TS_ASSERT_DELTA(orig_extra_conductivity_0, 7.0, 1e-9); // hard-coded using default } /* * Now we can make the modifier and apply it to the tissue using `SetConductivityModifier`. */ SimpleConductivityModifier modifier; p_bidomain_tissue->SetConductivityModifier( &modifier ); /* * To confirm that the conductivities have changed, let's iterate over all elements owned by this process * and check their conductivity against what we expect. */ for (AbstractTetrahedralMesh<2,2>::ElementIterator elt_iter=mesh.GetElementIteratorBegin(); elt_iter!=mesh.GetElementIteratorEnd(); ++elt_iter) { unsigned index = elt_iter->GetIndex(); if (index == 0u) { TS_ASSERT_DELTA(p_bidomain_tissue->rGetIntracellularConductivityTensor(0)(0,0), 3.14, 1e-9); TS_ASSERT_DELTA(p_bidomain_tissue->rGetExtracellularConductivityTensor(0)(0,0), 3.14, 1e-9); TS_ASSERT_DELTA(p_bidomain_tissue->rGetIntracellularConductivityTensor(0)(1,1), 0.707, 1e-9); TS_ASSERT_DELTA(p_bidomain_tissue->rGetExtracellularConductivityTensor(0)(1,1), 0.707, 1e-9); } else { TS_ASSERT_DELTA(p_bidomain_tissue->rGetIntracellularConductivityTensor(index)(0,0), 1.0*index*orig_intra_conductivity_0, 1e-9); TS_ASSERT_DELTA(p_bidomain_tissue->rGetExtracellularConductivityTensor(index)(0,0), 1.5*index*orig_extra_conductivity_0, 1e-9); } } }
void TestSimpleSimulation() throw(Exception) { /*Simulation parameters*/ HeartConfig::Instance()->SetSimulationDuration(0.7); //ms (falls over after this) HeartConfig::Instance()->SetUseAbsoluteTolerance(1e-6); //HeartConfig::Instance()->SetOdeTimeStep(0.01); const double width = 0.1; const double height = 0.1; const double depth = 0.1; const unsigned num_elem_x = 8; const double space_step = width/num_elem_x; /* Make the mesh*/ DistributedTetrahedralMesh<3,3> mesh; mesh.ConstructRegularSlabMesh(space_step, width, height, depth); /*Create a cell factory of the type we defined above. */ GeneralPlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 3> cell_factory(num_elem_x, width); /* monodomain problem class using (a pointer to) the cell factory */ BidomainProblem<3> problem( &cell_factory ); problem.SetMesh(&mesh); /* * HOW_TO_TAG Cardiac/Problem definition * Set discrete '''cuboid''' areas to have heterogeneous (intra- and/or extra-cellular) conductivity tensors. */ std::vector<ChasteCuboid<3> > input_areas; std::vector< c_vector<double,3> > intra_conductivities; std::vector< c_vector<double,3> > extra_conductivities; ChastePoint<3> corner_a(width/2, 0, 0); ChastePoint<3> corner_b(width, height, depth); input_areas.push_back(ChasteCuboid<3> (corner_a, corner_b)); //within the cuboid intra_conductivities.push_back( Create_c_vector(0.1, 0.1, 0.1) ); extra_conductivities.push_back( Create_c_vector(0.0, 0.0, 0.0) ); //This test should *fail* if you comment out the following line //(which blocks conductivity on the RHS of the slab). HeartConfig::Instance()->SetConductivityHeterogeneities(input_areas, intra_conductivities, extra_conductivities); //elsewhere HeartConfig::Instance()->SetIntracellularConductivities(Create_c_vector(1.2, 1.2, 1.2)); HeartConfig::Instance()->SetExtracellularConductivities(Create_c_vector(1.2, 1.2, 1.2)); /* set parameters*/ // HeartConfig::Instance()->SetSurfaceAreaToVolumeRatio(1.0); // HeartConfig::Instance()->SetCapacitance(1.0); /* Output Directory and prefix (for the hdf5 file), relative to CHASTE_TEST_OUTPUT*/ HeartConfig::Instance()->SetOutputDirectory("slab_results_het_halfcond"); HeartConfig::Instance()->SetOutputFilenamePrefix("Slab_small"); /* Initialise the problem*/ problem.Initialise(); /* Solve the PDE monodomain equaion*/ problem.Solve(); ReplicatableVector voltage_replicated(problem.GetSolution()); TS_ASSERT_EQUALS(mesh.GetNumNodes() * 2, voltage_replicated.GetSize()); unsigned lo, hi; lo = mesh.GetDistributedVectorFactory()->GetLow(); hi = mesh.GetDistributedVectorFactory()->GetHigh(); for (unsigned i=lo; i<hi; i++) { double x = mesh.GetNode(i)->rGetLocation()[0]; if (x<width/2) { //Left side is stimulated TS_ASSERT_LESS_THAN(-71.0,voltage_replicated[2 * i]); } else if (x>width/2) { //Right side is blocked TS_ASSERT_LESS_THAN(voltage_replicated[2 * i],-82.0); } } }
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)); }
void TestBidomainWithBathWithSvi() throw(Exception) { /* Make a 4x4 node mesh and set two interior elements to be bath elements */ DistributedTetrahedralMesh<2,2> mesh; mesh.ConstructRegularSlabMesh(0.04, 0.12, 0.12); for (AbstractTetrahedralMesh<2,2>::ElementIterator iter=mesh.GetElementIteratorBegin(); iter != mesh.GetElementIteratorEnd(); ++iter) { unsigned element_index = iter->GetIndex(); if ( element_index==10 || element_index==17 ) { iter->SetAttribute(HeartRegionCode::GetValidBathId()); } else { iter->SetAttribute(HeartRegionCode::GetValidTissueId()); } } HeartConfig::Instance()->SetSimulationDuration(10.0); // ms HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(0.001, 0.025, 0.25); HeartConfig::Instance()->SetIntracellularConductivities(Create_c_vector(1.75, 0.17)); HeartConfig::Instance()->SetExtracellularConductivities(Create_c_vector(7.0, 0.7)); ReplicatableVector final_solution_ici; ReplicatableVector final_solution_svi; // ICI - ionic current interpolation (the default) { HeartConfig::Instance()->SetOutputDirectory("BidomainWithBathIci2d"); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetUseStateVariableInterpolation(false); BathCellFactory cell_factory; BidomainWithBathProblem<2> bidomain_problem( &cell_factory ); bidomain_problem.SetMesh(&mesh); bidomain_problem.Initialise(); bidomain_problem.Solve(); final_solution_ici.ReplicatePetscVector(bidomain_problem.GetSolution()); } // SVI - state variable interpolation { HeartConfig::Instance()->SetOutputDirectory("BidomainWithBathSvi2d"); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetUseStateVariableInterpolation(true); BathCellFactory cell_factory; BidomainWithBathProblem<2> bidomain_problem( &cell_factory ); bidomain_problem.SetMesh(&mesh); bidomain_problem.Initialise(); bidomain_problem.Solve(); final_solution_svi.ReplicatePetscVector(bidomain_problem.GetSolution()); } // ICI TS_ASSERT_DELTA(final_solution_ici[15*2], 7.0918, 2e-3); // Node 15 phi_i TS_ASSERT_DELTA(final_solution_ici[15*2+1], 0.0401, 1e-3); // Node 15 phi_e // SVI TS_ASSERT_DELTA(final_solution_svi[15*2], 10.6217, 2e-3); // Node 15 phi_i TS_ASSERT_DELTA(final_solution_svi[15*2+1], -0.0180, 1e-3); // Node 15 phi_e }
void TestSolveCellSystemsInclUpdateVoltageWithNodeExchange() throw(Exception) { HeartConfig::Instance()->Reset(); DistributedTetrahedralMesh<1,1> mesh; mesh.ConstructRegularSlabMesh(1.0, 1.0); // [0,1] with h=0.1, ie 11 node mesh MyCardiacCellFactory cell_factory; cell_factory.SetMesh(&mesh); MonodomainTissue<1> monodomain_tissue( &cell_factory, true ); Vec voltage = PetscTools::CreateAndSetVec(2, -81.4354); // something that isn't resting potential monodomain_tissue.SolveCellSystems(voltage, 0, 1, false); // solve for 1ms without updating the voltage if (mesh.GetDistributedVectorFactory()->IsGlobalIndexLocal(0)) // Is node 0 locally owned? { TS_ASSERT_DELTA(monodomain_tissue.GetCardiacCell(0)->GetVoltage(), -81.4354, 1e-3); TS_ASSERT_DELTA(monodomain_tissue.GetCardiacCellOrHaloCell(1)->GetVoltage(), -81.4354, 1e-3); } if (mesh.GetDistributedVectorFactory()->IsGlobalIndexLocal(1)) // Is node 1 locally owned? { TS_ASSERT_DELTA(monodomain_tissue.GetCardiacCellOrHaloCell(0)->GetVoltage(), -81.4354, 1e-3); TS_ASSERT_DELTA(monodomain_tissue.GetCardiacCell(1)->GetVoltage(), -81.4354, 1e-3); } Vec voltage2 = PetscTools::CreateAndSetVec(2, -75); monodomain_tissue.SolveCellSystems(voltage2, 1, 2, true); // solve another ms, using this new voltage, but now updating the voltage too ReplicatableVector voltage2_repl(voltage2); // should have changed following solve // check the new voltage in the cell is NEAR -75 (otherwise the passed in voltage wasn't used, but // NOT EXACTLY -75, ie that the voltage was solved for. if (mesh.GetDistributedVectorFactory()->IsGlobalIndexLocal(0)) // Is node 0 locally owned? { // check has been updated TS_ASSERT_DIFFERS(monodomain_tissue.GetCardiacCell(0)->GetVoltage(), -75); // check near -75 TS_ASSERT_DELTA(monodomain_tissue.GetCardiacCell(0)->GetVoltage(), -75, 2.0); // within 2mV // check the passed in voltage was updated TS_ASSERT_DELTA(voltage2_repl[0], monodomain_tissue.GetCardiacCell(0)->GetVoltage(), 1e-10); } if (mesh.GetDistributedVectorFactory()->IsGlobalIndexLocal(1)) // Is node 1 locally owned? { TS_ASSERT_DIFFERS(monodomain_tissue.GetCardiacCell(1)->GetVoltage(), -75); TS_ASSERT_DELTA(monodomain_tissue.GetCardiacCell(1)->GetVoltage(), -75, 2.0); // within 2mV TS_ASSERT_DELTA(voltage2_repl[1], monodomain_tissue.GetCardiacCell(1)->GetVoltage(), 1e-10); } // now check the new voltages have been communicated // check the new voltage in the cell is NEAR -75 (otherwise the passed in voltage wasn't used, but // NOT EXACTLY -75, ie that the voltage was solved for. if (mesh.GetDistributedVectorFactory()->IsGlobalIndexLocal(0)) // Is node 0 locally owned? { TS_ASSERT_DIFFERS(monodomain_tissue.GetCardiacCellOrHaloCell(1)->GetVoltage(), -75); TS_ASSERT_DELTA(monodomain_tissue.GetCardiacCellOrHaloCell(1)->GetVoltage(), -75, 2.0); // within 2mV TS_ASSERT_DELTA(voltage2_repl[1], monodomain_tissue.GetCardiacCellOrHaloCell(1)->GetVoltage(), 1e-10); } if (mesh.GetDistributedVectorFactory()->IsGlobalIndexLocal(1)) // Is node 1 locally owned? { TS_ASSERT_DIFFERS(monodomain_tissue.GetCardiacCellOrHaloCell(0)->GetVoltage(), -75); TS_ASSERT_DELTA(monodomain_tissue.GetCardiacCellOrHaloCell(0)->GetVoltage(), -75, 2.0); // within 2mV TS_ASSERT_DELTA(voltage2_repl[0], monodomain_tissue.GetCardiacCellOrHaloCell(0)->GetVoltage(), 1e-10); } PetscTools::Destroy(voltage); PetscTools::Destroy(voltage2); }
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); }