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