void TestBidomainWithBathCanOutputVariables() throw(Exception) { HeartConfig::Instance()->SetSimulationDuration(0.01); //ms HeartConfig::Instance()->SetMeshFileName("mesh/test/data/1D_0_to_1_10_elements_with_two_attributes"); HeartConfig::Instance()->SetOutputDirectory("BidomainBathOutputVariables"); HeartConfig::Instance()->SetOutputFilenamePrefix("BidomainLR91_1d"); HeartConfig::Instance()->SetVisualizeWithMeshalyzer(); std::vector<std::string> output_variables; output_variables.push_back("cytosolic_calcium_concentration"); HeartConfig::Instance()->SetOutputVariables(output_variables); std::set<unsigned> tissue_ids; tissue_ids.insert(0); // Same as default value defined in HeartConfig std::set<unsigned> bath_ids; bath_ids.insert(1); HeartConfig::Instance()->SetTissueAndBathIdentifiers(tissue_ids, bath_ids); PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 1> cell_factory; BidomainWithBathProblem<1> bidomain_problem(&cell_factory); bidomain_problem.Initialise(); bidomain_problem.Solve(); FileFinder calcium_results("BidomainBathOutputVariables/output/BidomainLR91_1d_cytosolic_calcium_concentration.dat", RelativeTo::ChasteTestOutput); TS_ASSERT_EQUALS(calcium_results.IsFile(), true); FileFinder reference_results("heart/test/data/BidomainBathOutputVariables/BidomainLR91_1d_cytosolic_calcium_concentration.dat", RelativeTo::ChasteSourceRoot); NumericFileComparison comparer(calcium_results, reference_results); TS_ASSERT_EQUALS(comparer.CompareFiles(), true); }
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 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); }
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 TestFailsIfNoBathElements() throw (Exception) { HeartConfig::Instance()->SetSimulationDuration(1.0); //ms HeartConfig::Instance()->SetMeshFileName("mesh/test/data/1D_0_to_1_100_elements"); HeartConfig::Instance()->SetOutputDirectory("bidomain_bath"); HeartConfig::Instance()->SetOutputFilenamePrefix("BidomainLR91_1d"); PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 1> bidomain_cell_factory; BidomainWithBathProblem<1> bidomain_problem( &bidomain_cell_factory ); // Fails because no bath TS_ASSERT_THROWS_THIS(bidomain_problem.Initialise(), "No bath element found"); // Prevent an EventHandling exception in later tests HeartEventHandler::EndEvent(HeartEventHandler::EVERYTHING); }
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 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 TestLabellingNodes() throw (Exception) { HeartConfig::Instance()->SetSimulationDuration(0.01); //ms HeartConfig::Instance()->SetMeshFileName("mesh/test/data/1D_0_to_1_10_elements_with_two_attributes"); HeartConfig::Instance()->SetOutputDirectory("bidomain_bath"); HeartConfig::Instance()->SetOutputFilenamePrefix("BidomainLR91_1d"); PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 1> bidomain_cell_factory; BidomainWithBathProblem<1> bidomain_problem( &bidomain_cell_factory ); bidomain_problem.Initialise(); AbstractTetrahedralMesh<1,1>* p_mesh = &(bidomain_problem.rGetMesh()); // the middle 4 elements are 'heart' elements (ie region=0), // so the middle 5 nodes should be heart nodes char expected_node_regions[11]={ 'B', 'B', 'B', 'T', 'T', 'T', 'T', 'T', 'B','B','B'}; for (unsigned i=0; i<11; i++) { if (p_mesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(i)) { if (expected_node_regions[i] == 'B') { TS_ASSERT(HeartRegionCode::IsRegionBath( p_mesh->GetNode(i)->GetRegion()) ); } else { TS_ASSERT_EQUALS(expected_node_regions[i], 'T' ); TS_ASSERT(HeartRegionCode::IsRegionTissue( p_mesh->GetNode(i)->GetRegion()) ); } } } // we need to call solve as otherwise an EventHandler exception is thrown bidomain_problem.Solve(); }
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 TestBidomain3d() throw (Exception) { HeartEventHandler::Reset(); HeartConfig::Instance()->SetIntracellularConductivities(Create_c_vector(1.75, 1.75, 1.75)); HeartConfig::Instance()->SetExtracellularConductivities(Create_c_vector(7.0, 7.0, 7.0)); HeartConfig::Instance()->SetSimulationDuration(4.0); //ms HeartConfig::Instance()->SetMeshFileName("mesh/test/data/3D_0_to_1mm_6000_elements"); HeartConfig::Instance()->SetOutputDirectory("Bidomain3d"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain3d"); // Check the linear system can be solved to a low tolerance (in particular, checks the null space // stuff was implemented correctly HeartConfig::Instance()->SetUseAbsoluteTolerance(1e-14); PlaneStimulusCellFactory<CellLuoRudy1991FromCellML, 3> bidomain_cell_factory(-600.0*1000); BidomainProblem<3> bidomain_problem( &bidomain_cell_factory ); bidomain_problem.Initialise(); //bidomain_problem.SetNodeForAverageOfPhiZeroed(1330); //bidomain_problem.SetFixedExtracellularPotentialNodes(1330); bidomain_problem.Solve(); Vec voltage=bidomain_problem.GetSolution(); ReplicatableVector voltage_replicated; voltage_replicated.ReplicatePetscVector(voltage); /* * Test the top right node against the right one in the 1D case, * comparing voltage, and then test all the nodes on the right hand * face of the cube against the top right one, comparing voltage. */ bool need_initialisation = true; double probe_voltage=0; need_initialisation = true; // Test the RHF of the mesh for (AbstractTetrahedralMesh<3,3>::NodeIterator it = bidomain_problem.rGetMesh().GetNodeIteratorBegin(); it != bidomain_problem.rGetMesh().GetNodeIteratorEnd(); ++it) { if (it->GetPoint()[0] == 0.1) { // x = 0 is where the stimulus has been applied // x = 0.1cm is the other end of the mesh and where we want to // to test the value of the nodes if (need_initialisation) { probe_voltage = voltage_replicated[2*it->GetIndex()]; need_initialisation = false; } else { // the voltage at the end face varies a little because // of drift due to the orientation of the tets in the mesh, // hence the tolerance of 0.2 TS_ASSERT_DELTA(voltage_replicated[2*it->GetIndex()], probe_voltage, 0.2); } // if a 1D simulation is run for 4ms on the 0_1mm_10elements mesh // the result at the end node is 20.0755 TS_ASSERT_DELTA(voltage_replicated[2*it->GetIndex()], 20.0755, 1.3); } } /* * HOW_TO_TAG Cardiac/Output * Collect and print timings to benchmark different parts of the cardiac code. * * N.B. You may also want to use HeartEventHandler::Reset() if you call these again. */ HeartEventHandler::Headings(); HeartEventHandler::Report(); }
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 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 TestArchivingMeshFileWithAttributes() throw (Exception) { std::string archive_dir = "TestArchivingMeshFileWithAttributes"; { // save... HeartConfig::Instance()->SetSimulationDuration(0.01); //ms HeartConfig::Instance()->SetMeshFileName("mesh/test/data/1D_0_to_1_10_elements_with_three_attributes"); HeartConfig::Instance()->SetOutputDirectory(archive_dir + "Output"); HeartConfig::Instance()->SetOutputFilenamePrefix("BidomainLR91_1d"); std::set<unsigned> tissue_ids; tissue_ids.insert(0u); // (The default) std::set<unsigned> bath_ids; bath_ids.insert(1u); // (The default) bath_ids.insert(2u); // Some other type of bath HeartConfig::Instance()->SetTissueAndBathIdentifiers(tissue_ids,bath_ids); std::map<unsigned, double> multiple_bath_conductivities; multiple_bath_conductivities[1] = 3.14; multiple_bath_conductivities[2] = 2.72; HeartConfig::Instance()->SetBathMultipleConductivities(multiple_bath_conductivities); ZeroStimulusCellFactory<CellLuoRudy1991FromCellML, 1> bidomain_cell_factory; BidomainWithBathProblem<1> bidomain_problem( &bidomain_cell_factory ); bidomain_problem.Initialise(); // Save using helper class CardiacSimulationArchiver<BidomainWithBathProblem<1> >::Save(bidomain_problem, archive_dir, false); } HeartConfig::Instance()->Reset(); // Forget these IDs/conductivities, they should be loaded from the archive. { // load... AbstractCardiacProblem<1,1,2>* p_abstract_problem = CardiacSimulationArchiver<BidomainWithBathProblem<1> >::Load(archive_dir); // Check the identifiers have made it std::set<unsigned> tissue_ids = HeartConfig::Instance()->rGetTissueIdentifiers(); TS_ASSERT( tissue_ids.size() == 1u ); TS_ASSERT( *(tissue_ids.begin()) == 0u ); std::set<unsigned> bath_ids = HeartConfig::Instance()->rGetBathIdentifiers(); TS_ASSERT( bath_ids.size() == 2u ); TS_ASSERT( *bath_ids.begin() == 1u ); TS_ASSERT( *(++bath_ids.begin()) == 2u ); AbstractTetrahedralMesh<1,1>* p_mesh = &(p_abstract_problem->rGetMesh()); /* Check the element ids have been loaded properly. The middle 4 elements are 'heart' elements * (region=0). The edges are different "flavours" of bath... */ unsigned expected_element_regions[10]={ 2,1,1,0,0,0,0,1,1,2 }; for (AbstractTetrahedralMesh<1,1>::ElementIterator iter = p_mesh->GetElementIteratorBegin(); iter != p_mesh->GetElementIteratorEnd(); ++iter) { unsigned element_index = iter->GetIndex(); unsigned element_attribute = iter->GetUnsignedAttribute(); switch ( expected_element_regions[element_index] ) { case 0: TS_ASSERT(HeartRegionCode::IsRegionTissue( element_attribute )); break; case 1: TS_ASSERT_EQUALS( expected_element_regions[element_index], 1u); TS_ASSERT(HeartRegionCode::IsRegionBath( element_attribute )); TS_ASSERT_DELTA(HeartConfig::Instance()->GetBathConductivity( element_attribute ), 3.14, 1e-9); break; case 2: TS_ASSERT_EQUALS( expected_element_regions[element_index], 2u); TS_ASSERT(HeartRegionCode::IsRegionBath( element_attribute )); TS_ASSERT_DELTA(HeartConfig::Instance()->GetBathConductivity( element_attribute ), 2.72, 1e-9); break; default: NEVER_REACHED; break; }; } /* ...so the middle 5 nodes should be heart nodes */ char expected_node_regions[11]={ 'B', 'B', 'B', 'T', 'T', 'T', 'T', 'T', 'B','B','B'}; for (unsigned i=0; i<10; i++) { if (p_mesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(i)) { if ( expected_node_regions[i] == 'T') { TS_ASSERT(HeartRegionCode::IsRegionTissue( p_mesh->GetNode(i)->GetRegion() )); } else { TS_ASSERT_EQUALS( expected_node_regions[i], 'B') TS_ASSERT(HeartRegionCode::IsRegionBath( p_mesh->GetNode(i)->GetRegion() )); } } } delete p_abstract_problem; } }
void TestSettingElectrodesOnResumedSimulation(void) throw(Exception) { std::string archive_dir = "BidomainWithElectrodesArchiving"; // Create the mesh outside the save scope, so we can compare with the loaded version. TetrahedralMesh<2,2>* p_mesh = Load2dMeshAndSetCircularTissue<TetrahedralMesh<2,2> >( "mesh/test/data/2D_0_to_1mm_400_elements", 0.05, 0.05, 0.02); { // save HeartConfig::Instance()->SetSimulationDuration(3.0); // ms HeartConfig::Instance()->SetOutputDirectory(archive_dir + "Output"); HeartConfig::Instance()->SetOutputFilenamePrefix("bidomain_bath_2d_fluxes"); HeartConfig::Instance()->SetOdeTimeStep(0.001); // ms // need to create a cell factory but don't want any intra stim. ZeroStimulusCellFactory<CellTenTusscher2006EpiFromCellMLBackwardEuler, 2> cell_factory; BidomainWithBathProblem<2> bidomain_problem( &cell_factory ); bidomain_problem.SetMesh(p_mesh); bidomain_problem.Initialise(); // Save using helper class CardiacSimulationArchiver<BidomainWithBathProblem<2> >::Save(bidomain_problem, archive_dir, false); } { // load AbstractCardiacProblem<2,2,2>* p_abstract_problem = CardiacSimulationArchiver<BidomainWithBathProblem<2> >::Load(archive_dir); // get the new mesh AbstractTetrahedralMesh<2,2>& r_mesh = p_abstract_problem->rGetMesh(); TS_ASSERT_EQUALS(p_mesh->GetNumElements(), r_mesh.GetNumElements()); //boundary flux for Phi_e. -10e3 is under threshold, -14e3 crashes the cell model double boundary_flux = -11.0e3; double duration = 1.9; // of the stimulus, in ms double start_time = 0.5; // of the stimulus, in ms HeartConfig::Instance()->SetElectrodeParameters(false,0,boundary_flux, start_time, duration); p_abstract_problem->SetElectrodes(); // This should only generate action potential if the electrodes were correctly saved and restored. p_abstract_problem->Solve(); Vec sol = p_abstract_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<r_mesh.GetNumNodes(); i++) { // test V = 0 for all bath nodes and that an AP is triggered in the tissue if (HeartRegionCode::IsRegionBath(r_mesh.GetNode(i)->GetRegion())) { 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; } } // We can get away with the following line only because this is a friend class and test. boost::shared_ptr<Electrodes<2> > p_electrodes = static_cast<BidomainWithBathProblem<2>* >(p_abstract_problem)->mpElectrodes; TS_ASSERT_EQUALS(p_electrodes->mAreActive, false); // should be switched off by now.. TS_ASSERT_EQUALS(ap_triggered, true); delete p_abstract_problem; } delete p_mesh; }
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 TestConductionVelocityInCrossFibreDirection2d() throw(Exception) { ReplicatableVector final_solution_ici; ReplicatableVector final_solution_svi; HeartConfig::Instance()->SetSimulationDuration(4.0); //ms HeartConfig::Instance()->SetOdePdeAndPrintingTimeSteps(0.005, 0.01, 0.01); // much lower conductivity in cross-fibre direction - ICI will struggle HeartConfig::Instance()->SetIntracellularConductivities(Create_c_vector(1.75, 0.17)); HeartConfig::Instance()->SetExtracellularConductivities(Create_c_vector(7.0, 0.7)); // ICI - nodal current interpolation - the default { TetrahedralMesh<2,2> mesh; mesh.ConstructRegularSlabMesh(0.02 /*h*/, 0.5, 0.3); HeartConfig::Instance()->SetOutputDirectory("BidomainIci2d"); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetUseStateVariableInterpolation(false); BlockCellFactory<2> cell_factory; BidomainProblem<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 { TetrahedralMesh<2,2> mesh; mesh.ConstructRegularSlabMesh(0.02 /*h*/, 0.5, 0.3); HeartConfig::Instance()->SetOutputDirectory("BidomainSvi2d"); HeartConfig::Instance()->SetOutputFilenamePrefix("results"); HeartConfig::Instance()->SetUseStateVariableInterpolation(true); BlockCellFactory<2> cell_factory; BidomainProblem<2> bidomain_problem( &cell_factory ); bidomain_problem.SetMesh(&mesh); bidomain_problem.Initialise(); bidomain_problem.Solve(); final_solution_svi.ReplicatePetscVector(bidomain_problem.GetSolution()); } // See comments in equivalent part of test/monodomain/TestMonodomainWithSvi.hpp // For bidomain with h=0.02, SVI CV is slower in both fibre and cross-fibre // directions // node 20 (for h=0.02) is on the x-axis (fibre direction) TS_ASSERT_DELTA(final_solution_ici[20*2], -64.1105, 1e-3); // Node 20 phi_i TS_ASSERT_DELTA(final_solution_svi[20*2], -78.0936, 1e-3); // Node 20 phi_i // node 234 (for h=0.02) is on the y-axis (cross-fibre direction) TS_ASSERT_DELTA(final_solution_ici[234*2], -57.7239, 1e-3); // Node 234 phi_i TS_ASSERT_DELTA(final_solution_svi[234*2], 38.9004, 1e-3); // Node 234 phi_i }
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); } } }
// 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); }
// 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 } }
// 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); }
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 TestBidomain3d() throw (Exception) { HeartConfig::Instance()->SetIntracellularConductivities(Create_c_vector(1.75, 1.75, 1.75)); HeartConfig::Instance()->SetExtracellularConductivities(Create_c_vector(7.0, 7.0, 7.0)); HeartConfig::Instance()->SetSimulationDuration(150.0); //ms //Note that we can only call the old permute nodes funcutionality on the sequential mesh object //HeartConfig::Instance()->SetMeshFileName("mesh/test/data/3D_0_to_.5mm_1889_elements_irregular"); BidomainFaceStimulusCellFactory bidomain_cell_factory; BidomainProblem<3> bidomain_problem( &bidomain_cell_factory ); TetrahedralMesh<3,3> mesh; TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/3D_0_to_.5mm_1889_elements_irregular"); mesh.ConstructFromMeshReader(mesh_reader); bidomain_problem.SetMesh(&mesh); bidomain_problem.PrintOutput(false); HeartConfig::Instance()->SetKSPSolver("symmlq"); HeartConfig::Instance()->SetKSPPreconditioner("bjacobi"); PetscOptionsSetValue("-log_summary", ""); bidomain_problem.Initialise(); //Mesh isn't actually loaded until initialise method is called RandomNumberGenerator::Instance(); bidomain_problem.rGetMesh().PermuteNodes(); RandomNumberGenerator::Destroy(); bidomain_problem.Solve(); Vec voltage=bidomain_problem.GetSolution(); ReplicatableVector voltage_replicated; voltage_replicated.ReplicatePetscVector(voltage); /* * Test the top right node against the right one in the 1D case, * comparing voltage, and then test all the nodes on the right hand * face of the cube against the top right one, comparing voltage. */ bool need_initialisation = true; double probe_voltage=-9999.; need_initialisation = true; // Test the RHF of the mesh for (unsigned i = 0; i < bidomain_problem.rGetMesh().GetNumNodes(); i++) { if (bidomain_problem.rGetMesh().GetNode(i)->GetPoint()[0] == 0.05) { // x = 0 is where the stimulus has been applied // x = 0.05cm is the other end of the mesh and where we want to // to test the value of the nodes if (need_initialisation) { probe_voltage = voltage_replicated[2*i]; need_initialisation = false; } else { // the voltage at the end face varies a little because // of drift due to the orientation of the tets in the mesh, // hence the tolerance of 0.02 TS_ASSERT_DELTA(voltage_replicated[2*i], probe_voltage, 0.02); } // Check against hard coded value // For 50 ms test TS_ASSERT_DELTA(voltage_replicated[2*i], 7.3, 0.2); // For 150 ms test TS_ASSERT_DELTA(voltage_replicated[2*i], -1.735, 0.1); } } }
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); } } }