SIM* CellBasedSimulationArchiver<ELEMENT_DIM, SIM, SPACE_DIM>::Load(const std::string& rArchiveDirectory, const double& rTimeStamp) { /** * Find the right archive (and mesh) to load. The files are contained within * the 'archive' folder in rArchiveDirectory, with the archive itself called * 'cell_population_sim_at_time_`rTimeStamp`.arch'. The path to this file is returned. * * The path to the mesh is stored in ArchiveLocationInfo for use by the * CellPopulation de-serialization routines. */ std::ostringstream time_stamp; time_stamp << rTimeStamp; std::string archive_filename = "cell_population_sim_at_time_" + time_stamp.str() + ".arch"; std::string mesh_filename = "mesh_" + time_stamp.str(); FileFinder archive_dir(rArchiveDirectory + "/archive/", RelativeTo::ChasteTestOutput); ArchiveLocationInfo::SetMeshPathname(archive_dir, mesh_filename); // Create an input archive ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_filename); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); // Load the simulation SIM* p_sim; (*p_arch) >> p_sim; return p_sim; }
static void archive_dir(const std::string& path, const std::string& dirname, config& cfg, std::pair<std::vector<std::string>, std::vector<std::string> >& ignore_patterns) { cfg["name"] = dirname; const std::string dir = path + '/' + dirname; std::vector<std::string> files, dirs; get_files_in_dir(dir,&files,&dirs); for(std::vector<std::string>::const_iterator i = files.begin(); i != files.end(); ++i) { bool valid = !looks_like_pbl(*i); for(std::vector<std::string>::const_iterator p = ignore_patterns.first.begin(); p != ignore_patterns.first.end(); ++p) { if (utils::wildcard_string_match(*i, *p)) { valid = false; break; } } if (valid) { archive_file(dir,*i,cfg.add_child("file")); } } for(std::vector<std::string>::const_iterator j = dirs.begin(); j != dirs.end(); ++j) { bool valid = true; for(std::vector<std::string>::const_iterator p = ignore_patterns.second.begin(); p != ignore_patterns.second.end(); ++p) { if (utils::wildcard_string_match(*j, *p)) { valid = false; break; } } if (valid) { archive_dir(dir,*j,cfg.add_child("dir"),ignore_patterns); } } }
void archive_addon(const std::string& addon_name, config& cfg) { const std::string parentd = filesystem::get_addons_dir(); std::pair<std::vector<std::string>, std::vector<std::string> > ignore_patterns; ignore_patterns = read_ignore_patterns(addon_name); archive_dir(parentd, addon_name, cfg.add_child("dir"), ignore_patterns); }
void TestVolumeDependentAveragedSourcePdeArchiving() throw(Exception) { EXIT_IF_PARALLEL; // Set up simulation time SimulationTime::Instance()->SetEndTimeAndNumberOfTimeSteps(1.0, 1); // Set up cell population HoneycombMeshGenerator generator(5, 5, 0); MutableMesh<2,2>* p_generating_mesh = generator.GetMesh(); NodesOnlyMesh<2> mesh; mesh.ConstructNodesWithoutMesh(*p_generating_mesh, 1.5); std::vector<CellPtr> cells; CellsGenerator<FixedDurationGenerationBasedCellCycleModel, 2> cells_generator; cells_generator.GenerateBasic(cells, mesh.GetNumNodes()); NodeBasedCellPopulation<2> cell_population(mesh, cells); FileFinder archive_dir("archive", RelativeTo::ChasteTestOutput); std::string archive_file = "VolumeDependentAveragedSourcePde.arch"; ArchiveLocationInfo::SetMeshFilename("VolumeDependentAveragedSourcePde"); { // Create a PDE object AbstractLinearEllipticPde<2,2>* const p_pde = new VolumeDependentAveragedSourcePde<2>(cell_population, 0.05); // Create output archive and archive PDE object ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); (*p_arch) << p_pde; // Avoid memory leak delete p_pde; } { AbstractLinearEllipticPde<2,2>* p_pde; // Create an input archive and restore PDE object from archive ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); (*p_arch) >> p_pde; // Test that the PDE and its member variables were archived correctly TS_ASSERT(dynamic_cast<VolumeDependentAveragedSourcePde<2>*>(p_pde) != NULL); VolumeDependentAveragedSourcePde<2>* p_static_cast_pde = static_cast<VolumeDependentAveragedSourcePde<2>*>(p_pde); TS_ASSERT_DELTA(p_static_cast_pde->GetCoefficient(), 0.05, 1e-6); TS_ASSERT_EQUALS(p_static_cast_pde->mrCellPopulation.GetNumRealCells(), 25u); TS_ASSERT(p_static_cast_pde->mpStaticCastCellPopulation != NULL); TS_ASSERT_EQUALS(p_static_cast_pde->mpStaticCastCellPopulation->GetNumRealCells(), 25u); // Avoid memory leaks delete &(p_static_cast_pde->mrCellPopulation); delete p_pde; } }
void archive_addon(const std::string& addon_name, config& cfg) { const std::string parentd = get_addon_campaigns_dir(); std::pair<std::vector<std::string>, std::vector<std::string> > ignore_patterns; // External .cfg may not exist; newer campaigns have a _main.cfg const std::string external_cfg = addon_name + ".cfg"; if (file_exists(parentd + "/" + external_cfg)) { archive_file(parentd, external_cfg, cfg.add_child("file")); } ignore_patterns = read_ignore_patterns(addon_name); archive_dir(parentd, addon_name, cfg.add_child("dir"), ignore_patterns); }
void TestArchiveOpenerReadAndWrite() throw(Exception) { // Should this test fail with an exception involving // apps/texttest/chaste/resume_bidomain/save_bidomain // then look at TestCardiacSimulationArchiver mArchiveDir = "archiving_helpers"; FileFinder archive_dir(mArchiveDir, RelativeTo::ChasteTestOutput); std::string archive_file = "archive_opener.arch"; const unsigned test_int = 123; // Write { OutputArchiveOpener archive_opener_out(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = archive_opener_out.GetCommonArchive(); boost::archive::text_oarchive* p_process_arch = ProcessSpecificArchive<boost::archive::text_oarchive>::Get(); (*p_arch) & test_int; // All can write to the common archive - non-masters will write to /dev/null. (*p_process_arch) & test_int; // archive_opener_out will do a PetscTools::Barrier when it is destructed } // Read { TS_ASSERT_THROWS_THIS(ProcessSpecificArchive<boost::archive::text_oarchive>::Get(), "A ProcessSpecificArchive has not been set up."); TS_ASSERT_THROWS_THIS(ProcessSpecificArchive<boost::archive::text_iarchive>::Get(), "A ProcessSpecificArchive has not been set up."); InputArchiveOpener archive_opener_in(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = archive_opener_in.GetCommonArchive(); boost::archive::text_iarchive* p_process_arch = ProcessSpecificArchive<boost::archive::text_iarchive>::Get(); unsigned test_int1, test_int2; (*p_arch) & test_int1; (*p_process_arch) & test_int2; TS_ASSERT_EQUALS(test_int1, test_int); TS_ASSERT_EQUALS(test_int2, test_int); } // Cover the case of an archive in the chaste folder (i.e. a path relative to the working directory) if (PetscTools::IsSequential()) { // Read FileFinder save_bidomain_dir("apps/texttest/chaste/resume_bidomain/save_bidomain", RelativeTo::ChasteSourceRoot); InputArchiveOpener archive_opener_relative(save_bidomain_dir, "archive.arch"); } PetscTools::Barrier(); // Make sure all processes have finished this test before proceeding }
void TestOpenFutureBoostArchive() throw (Exception) { //Check testout/archive/specific_secondary.arch FileFinder archive_dir("global/test/data", RelativeTo::ChasteSourceRoot); std::string archive_file = "future_boost.arch"; // future_boost has got archive version 14 in it // 33 => 3 // 34 => 4 // 36 => 5 // 37 => 5 // 40 => 5 // 42 => 7 // 46 => 9 // 48 => 9 // 49 => 9 // 51 => 9 // 52 => ?? // 53 => 10 // 54 => 10 // 55 => 10 // 56 => 11 // 57 => 11 // 58 => 12 // 59 => 13 #ifndef BOOST_VERSION TS_FAIL("This test needs to know the version of Boost with which it was compiled."); return; #endif //#if BOOST_VERSION >= 999999 // InputArchiveOpener archive_opener_in(archive_dir, archive_file, 0); // boost::archive::text_iarchive* p_arch = archive_opener_in.GetCommonArchive(); // boost::archive::text_iarchive* p_process_arch = ProcessSpecificArchive<boost::archive::text_iarchive>::Get(); // // const unsigned test_int = 321; // unsigned test_int1, test_int2; // (*p_arch) & test_int1; // (*p_process_arch) & test_int2; // // TS_ASSERT_EQUALS(test_int1, test_int); // TS_ASSERT_EQUALS(test_int2, 0u); //#else //Current Boost can't read this archive... TS_ASSERT_THROWS_CONTAINS(InputArchiveOpener archive_opener_in(archive_dir, archive_file, 0), "Could not open Boost archive '"); //#endif }
void TestSpecifyingSecondaryArchive() throw (Exception) { FileFinder archive_dir("archive", RelativeTo::ChasteTestOutput); std::string archive_file = "specific_secondary.arch"; const unsigned test_int = 321; const unsigned proc_id = PetscTools::GetMyRank(); // Writing when specifying the secondary archive doesn't make sense { TS_ASSERT_THROWS_THIS(OutputArchiveOpener archive_opener_out(archive_dir, archive_file, UINT_MAX), "Specifying the secondary archive file ID doesn't make sense when writing."); } // Write normally so we can test reading { OutputArchiveOpener archive_opener_out(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = archive_opener_out.GetCommonArchive(); boost::archive::text_oarchive* p_process_arch = ProcessSpecificArchive<boost::archive::text_oarchive>::Get(); (*p_arch) & test_int; // All can write to the common archive - non-masters will write to /dev/null. (*p_process_arch) & proc_id; // archive_opener_out will do a PetscTools::Barrier when it is destructed } // Read { TS_ASSERT_THROWS_THIS(ProcessSpecificArchive<boost::archive::text_oarchive>::Get(), "A ProcessSpecificArchive has not been set up."); TS_ASSERT_THROWS_THIS(ProcessSpecificArchive<boost::archive::text_iarchive>::Get(), "A ProcessSpecificArchive has not been set up."); InputArchiveOpener archive_opener_in(archive_dir, archive_file, 0); boost::archive::text_iarchive* p_arch = archive_opener_in.GetCommonArchive(); boost::archive::text_iarchive* p_process_arch = ProcessSpecificArchive<boost::archive::text_iarchive>::Get(); unsigned test_int1, test_int2; (*p_arch) & test_int1; (*p_process_arch) & test_int2; TS_ASSERT_EQUALS(test_int1, test_int); TS_ASSERT_EQUALS(test_int2, 0u); } }
void CellBasedSimulationArchiver<ELEMENT_DIM, SIM, SPACE_DIM>::Save(SIM* pSim) { // Get the simulation time as a string const SimulationTime* p_sim_time = SimulationTime::Instance(); assert(p_sim_time->IsStartTimeSetUp()); std::ostringstream time_stamp; time_stamp << p_sim_time->GetTime(); // Set up folder and filename of archive FileFinder archive_dir(pSim->GetOutputDirectory() + "/archive/", RelativeTo::ChasteTestOutput); std::string archive_filename = "cell_population_sim_at_time_" + time_stamp.str() + ".arch"; ArchiveLocationInfo::SetMeshFilename(std::string("mesh_") + time_stamp.str()); // Create output archive ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_filename); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); // Archive the simulation (const-ness would be a pain here) (*p_arch) & pSim; }
void TestSimpleUniformSourceParabolicPdeArchiving() throw(Exception) { FileFinder archive_dir("archive", RelativeTo::ChasteTestOutput); std::string archive_file = "SimpleUniformSourceParabolicPde.arch"; ArchiveLocationInfo::SetMeshFilename("SimpleUniformSourceParabolicPde"); { // Create a PDE object AbstractLinearParabolicPde<2,2>* const p_pde = new SimpleUniformSourceParabolicPde<2>(0.1); // Create output archive and archive PDE object ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); (*p_arch) << p_pde; delete p_pde; } { AbstractLinearParabolicPde<2,2>* p_pde; // Create an input archive and restore PDE object from archive ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); (*p_arch) >> p_pde; // Test that the PDE and its member variables were archived correctly TS_ASSERT(dynamic_cast<SimpleUniformSourceParabolicPde<2>*>(p_pde) != NULL); SimpleUniformSourceParabolicPde<2>* p_static_cast_pde = static_cast<SimpleUniformSourceParabolicPde<2>*>(p_pde); TS_ASSERT_EQUALS(p_static_cast_pde->GetCoefficient(),0.1); // Avoid memory leaks delete p_pde; } }
void TestArchiveDiagonalVertexBasedDivisionRule() throw(Exception) { FileFinder archive_dir("archive", RelativeTo::ChasteTestOutput); std::string archive_file = "division_rules.arch"; // Create data structures to store variables to test for equality here { boost::shared_ptr<AbstractVertexBasedDivisionRule<2> > p_division_rule(new DiagonalVertexBasedDivisionRule<2>()); // Create output archive ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); // Record values to test into data structures // If necessary you can use static_cast<ConcreteClass*>(p_abstract_class) // (if your abstract class doesn't contain the necessary variables and methods) (*p_arch) << p_division_rule; } { boost::shared_ptr<AbstractVertexBasedDivisionRule<2> > p_division_rule; // Create an input archive ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); // restore from the archive (*p_arch) >> p_division_rule; // Check things in the data structures with TS_ASSERTS here. // If necessary you can use static_cast<ConcreteClass*>(p_abstract_class_2) // (if your abstract class doesn't contain the necessary variables and methods) // Check that we have got back the right kind of division rule. TS_ASSERT(dynamic_cast <DiagonalVertexBasedDivisionRule<2>* > (p_division_rule.get())); } }
void TestMonodomainTissueUsingPurkinjeCellFactory() throw(Exception) { HeartConfig::Instance()->Reset(); TrianglesMeshReader<2,2> reader("mesh/test/data/mixed_dimension_meshes/2D_0_to_1mm_200_elements"); MixedDimensionMesh<2,2> mixed_mesh; mixed_mesh.ConstructFromMeshReader(reader); PurkinjeCellFactory cell_factory; cell_factory.SetMesh(&mixed_mesh); MonodomainTissue<2> tissue( &cell_factory ); TS_ASSERT(tissue.HasPurkinje()); TS_ASSERT_EQUALS(tissue.rGetPurkinjeCellsDistributed().size(), tissue.rGetCellsDistributed().size()); TS_ASSERT_EQUALS(tissue.rGetPurkinjeIionicCacheReplicated().GetSize(), tissue.rGetIionicCacheReplicated().GetSize()); for (AbstractTetrahedralMesh<2,2>::NodeIterator current_node = mixed_mesh.GetNodeIteratorBegin(); current_node != mixed_mesh.GetNodeIteratorEnd(); ++current_node) { unsigned global_index = current_node->GetIndex(); AbstractCardiacCellInterface* p_purkinje_cell = tissue.GetPurkinjeCell(global_index); double y = current_node->rGetLocation()[1]; // cable nodes are on y=0.05 (we don't test by index because indices may be permuted in parallel). if( fabs(y-0.05) < 1e-8 ) { TS_ASSERT(dynamic_cast<CellDiFrancescoNoble1985FromCellML*>(p_purkinje_cell) != NULL); } else { TS_ASSERT(dynamic_cast<FakeBathCell*>(p_purkinje_cell) != NULL); } TS_ASSERT_EQUALS(tissue.rGetPurkinjeCellsDistributed()[global_index-mixed_mesh.GetDistributedVectorFactory()->GetLow()], p_purkinje_cell); } // Test archiving too FileFinder archive_dir("monodomain_tissue_purkinje_archive", RelativeTo::ChasteTestOutput); std::string archive_file = "monodomain_tissue_purkinje.arch"; { // Save to archive ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); // Make sure at least one Purkinje cell has a non-initial-condition state variable to compare if (mixed_mesh.GetDistributedVectorFactory()->IsGlobalIndexLocal(0u)) { tissue.GetPurkinjeCell(0u)->SetVoltage(1234.5); } AbstractCardiacTissue<2>* const p_archive_tissue = &tissue; (*p_arch) << p_archive_tissue; } { // Load from archive and compare ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); AbstractCardiacTissue<2>* p_tissue; (*p_arch) >> p_tissue; TS_ASSERT(p_tissue->HasPurkinje()); TS_ASSERT_EQUALS(p_tissue->rGetPurkinjeCellsDistributed().size(), tissue.rGetPurkinjeCellsDistributed().size()); TS_ASSERT_EQUALS(p_tissue->rGetPurkinjeIionicCacheReplicated().GetSize(), tissue.rGetPurkinjeIionicCacheReplicated().GetSize()); for (AbstractTetrahedralMesh<2,2>::NodeIterator current_node = p_tissue->mpMesh->GetNodeIteratorBegin(); current_node != p_tissue->mpMesh->GetNodeIteratorEnd(); ++current_node) { unsigned global_index = current_node->GetIndex(); AbstractCardiacCellInterface* p_purkinje_cell = p_tissue->GetPurkinjeCell(global_index); double y = current_node->rGetLocation()[1]; // cable nodes are on y=0.05 (we don't test by index because indices may be permuted in parallel). if( fabs(y-0.05) < 1e-8 ) { TS_ASSERT(dynamic_cast<CellDiFrancescoNoble1985FromCellML*>(p_purkinje_cell) != NULL); } else { TS_ASSERT(dynamic_cast<FakeBathCell*>(p_purkinje_cell) != NULL); } TS_ASSERT_EQUALS(p_purkinje_cell->GetVoltage(), tissue.GetPurkinjeCell(global_index)->GetVoltage()); TS_ASSERT_EQUALS(p_tissue->rGetPurkinjeCellsDistributed()[global_index-p_tissue->mpMesh->GetDistributedVectorFactory()->GetLow()], p_purkinje_cell); } delete p_tissue; } const std::string migration_archive_dir("TestMonodomainTissue/purkinje_migration_archive"); { // Save via MonodomainProblem so we can migrate // Note: from Chaste release 3.1 onward we no longer support Boost 1.33. // The earliest version of Boost supported in 1.34 // Run the test with b=_hostconfig,boost=1-34_5 to save /* scons b=_hostconfig,boost=1-34_5 ts=heart/test/monodomain/TestMonodomainTissue.hpp * */ MonodomainProblem<2> monodomain_problem( &cell_factory ); monodomain_problem.SetMesh(&mixed_mesh); monodomain_problem.Initialise(); TS_ASSERT(monodomain_problem.GetMonodomainTissue()->HasPurkinje()); CardiacSimulationArchiver<MonodomainProblem<2> >::Save(monodomain_problem, migration_archive_dir); } TS_ASSERT_EQUALS(tissue.rGetPurkinjeIionicCacheReplicated().GetSize(), 121u); }
void TestArchiving() throw(Exception) { FileFinder archive_dir("archive", RelativeTo::ChasteTestOutput); std::string archive_file = "potts_cell_population_2d.arch"; // The following line is required because the loading of a cell population // is usually called by the method CellBasedSimulation::Load() ArchiveLocationInfo::SetMeshFilename("potts_mesh_2d"); // Create mesh PottsMeshReader<2> mesh_reader("cell_based/test/data/TestPottsMeshWriter/potts_mesh_2d"); PottsMesh<2> mesh; mesh.ConstructFromMeshReader(mesh_reader); // Archive cell population { // Need to set up time unsigned num_steps = 10; SimulationTime* p_simulation_time = SimulationTime::Instance(); p_simulation_time->SetEndTimeAndNumberOfTimeSteps(1.0, num_steps+1); // Create a Potts-based cell population object std::vector<CellPtr> cells; CellsGenerator<FixedDurationGenerationBasedCellCycleModel, 2> cells_generator; cells_generator.GenerateBasic(cells, mesh.GetNumElements()); // Create cell population AbstractCellPopulation<2>* const p_cell_population = new PottsBasedCellPopulation<2>(mesh, cells); // Cells have been given birth times of 0, -1, -2, -3, -4. // loop over them to run to time 0.0; for (AbstractCellPopulation<2>::Iterator cell_iter = p_cell_population->Begin(); cell_iter != p_cell_population->End(); ++cell_iter) { cell_iter->ReadyToDivide(); } // Create an update rule and pass to the population MAKE_PTR(VolumeConstraintPottsUpdateRule<2>, p_volume_constraint_update_rule); static_cast<PottsBasedCellPopulation<2>*>(p_cell_population)->AddUpdateRule(p_volume_constraint_update_rule); // Create output archive ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); // Set member variables in order to test that they are archived correctly static_cast<PottsBasedCellPopulation<2>*>(p_cell_population)->SetTemperature(0.25); static_cast<PottsBasedCellPopulation<2>*>(p_cell_population)->SetNumSweepsPerTimestep(3); static_cast<PottsBasedCellPopulation<2>*>(p_cell_population)->SetUpdateNodesInRandomOrder(false); static_cast<PottsBasedCellPopulation<2>*>(p_cell_population)->SetIterateRandomlyOverUpdateRuleCollection(true); // Archive the cell population (*p_arch) << static_cast<const SimulationTime&>(*p_simulation_time); (*p_arch) << p_cell_population; // Tidy up SimulationTime::Destroy(); delete p_cell_population; } // Restore cell population { // Need to set up time unsigned num_steps = 10; SimulationTime* p_simulation_time = SimulationTime::Instance(); p_simulation_time->SetStartTime(0.0); p_simulation_time->SetEndTimeAndNumberOfTimeSteps(1.0, num_steps+1); p_simulation_time->IncrementTimeOneStep(); AbstractCellPopulation<2>* p_cell_population; // Create an input archive ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); // Restore the cell population (*p_arch) >> *p_simulation_time; (*p_arch) >> p_cell_population; // Test that the member variables have been archived correctly PottsBasedCellPopulation<2>* p_static_population = static_cast<PottsBasedCellPopulation<2>*>(p_cell_population); TS_ASSERT_DELTA(p_static_population->GetTemperature(), 0.25, 1e-6); TS_ASSERT_EQUALS(p_static_population->GetNumSweepsPerTimestep(), 3u); TS_ASSERT_EQUALS(p_static_population->GetUpdateNodesInRandomOrder(), false); TS_ASSERT_EQUALS(p_static_population->GetIterateRandomlyOverUpdateRuleCollection(), true); // Test that the update rule has been archived correctly std::vector<boost::shared_ptr<AbstractPottsUpdateRule<2> > > update_rule_collection = p_static_population->rGetUpdateRuleCollection(); TS_ASSERT_EQUALS(update_rule_collection.size(), 1u); TS_ASSERT_EQUALS((*update_rule_collection[0]).GetIdentifier(), "VolumeConstraintPottsUpdateRule-2"); // Tidy up delete p_cell_population; } }
void TestArchiving() throw (Exception) { FileFinder archive_dir("archive", RelativeTo::ChasteTestOutput); std::string archive_file = "cylindrical_vertex_mesh_base.arch"; ArchiveLocationInfo::SetMeshFilename("cylindrical_vertex_mesh"); // Create mesh unsigned num_cells_across = 4; unsigned num_cells_up = 7; CylindricalHoneycombVertexMeshGenerator generator(num_cells_across, num_cells_up); AbstractMesh<2,2>* const p_saved_mesh = generator.GetCylindricalMesh(); double crypt_width = num_cells_across; /* * You need the const above to stop a BOOST_STATIC_ASSERTION failure. * This is because the serialization library only allows you to save * tracked objects while the compiler considers them const, to prevent * the objects changing during the save, and so object tracking leading * to wrong results. For example, A is saved once via pointer, then * changed, then saved again. The second save notes that A was saved * before, so doesn't write its data again, and the change is lost. */ { // Serialize the mesh TS_ASSERT_DELTA((static_cast<Cylindrical2dVertexMesh*>(p_saved_mesh))->GetWidth(0), crypt_width, 1e-7); // Create output archive ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); // We have to serialize via a pointer here, or the derived class information is lost. (*p_arch) << p_saved_mesh; } { // De-serialize and compare AbstractMesh<2,2>* p_loaded_mesh; // Create an input archive ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); // Restore from the archive (*p_arch) >> p_loaded_mesh; // Compare the loaded mesh against the original Cylindrical2dVertexMesh* p_mesh2 = static_cast<Cylindrical2dVertexMesh*>(p_loaded_mesh); Cylindrical2dVertexMesh* p_mesh = static_cast<Cylindrical2dVertexMesh*>(p_saved_mesh); // Compare width TS_ASSERT_DELTA(p_mesh2->GetWidth(0), crypt_width, 1e-7); TS_ASSERT_DELTA(p_mesh->GetWidth(0), crypt_width, 1e-7); // Compare nodes TS_ASSERT_EQUALS(p_mesh->GetNumNodes(), p_mesh2->GetNumNodes()); for (unsigned i=0; i<p_mesh->GetNumNodes(); i++) { Node<2>* p_node = p_mesh->GetNode(i); Node<2>* p_node2 = p_mesh2->GetNode(i); TS_ASSERT_EQUALS(p_node->IsDeleted(), p_node2->IsDeleted()); TS_ASSERT_EQUALS(p_node->GetIndex(), p_node2->GetIndex()); TS_ASSERT_EQUALS(p_node->IsBoundaryNode(), p_node2->IsBoundaryNode()); for (unsigned j=0; j<2; j++) { TS_ASSERT_DELTA(p_node->rGetLocation()[j], p_node2->rGetLocation()[j], 1e-4); } } // Compare elements TS_ASSERT_EQUALS(p_mesh->GetNumElements(), p_mesh2->GetNumElements()); TS_ASSERT_EQUALS(p_mesh->GetNumAllElements(), p_mesh2->GetNumAllElements()); for (unsigned i=0; i<p_mesh->GetNumElements(); i++) { VertexElement<2,2>* p_elt = p_mesh->GetElement(i); VertexElement<2,2>* p_elt2 = p_mesh2->GetElement(i); TS_ASSERT_EQUALS(p_elt->GetNumNodes(), p_elt2->GetNumNodes()); for (unsigned j=0; j<p_elt->GetNumNodes(); j++) { TS_ASSERT_EQUALS(p_elt->GetNodeGlobalIndex(j), p_elt2->GetNodeGlobalIndex(j)); } } // Tidy up delete p_mesh2; } }
void TestArchiving() throw (Exception) { // Set archiving location FileFinder archive_dir("archive", RelativeTo::ChasteTestOutput); std::string archive_file = "mvmwr.arch"; ArchiveLocationInfo::SetMeshFilename("mvmwr"); // Create mesh MutableVertexMesh<2,2>* p_mesh = ConstructFiveCellRosette(); // Set member variables p_mesh->SetProtorosetteFormationProbability(0.123); p_mesh->SetProtorosetteResolutionProbabilityPerTimestep(0.234); p_mesh->SetRosetteResolutionProbabilityPerTimestep(0.345); AbstractMesh<2,2>* const p_abstract_mesh = p_mesh; /* * You need the const above to stop a BOOST_STATIC_ASSERTION failure. * This is because the serialization library only allows you to save tracked * objects while the compiler considers them const, to prevent the objects * changing during the save, and so object tracking leading to wrong results. * * E.g. A is saved once via pointer, then changed, then saved again. The second * save notes that A was saved before, so doesn't write its data again, and the * change is lost. */ // Create an output archive { TS_ASSERT_EQUALS((static_cast<MutableVertexMesh<2,2>*>(p_abstract_mesh))->GetNumNodes(), 11u); TS_ASSERT_EQUALS((static_cast<MutableVertexMesh<2,2>*>(p_abstract_mesh))->GetNumElements(), 5u); // Create output archive ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); // We have to serialize via a pointer here, or the derived class information is lost (*p_arch) << p_abstract_mesh; } { // De-serialize and compare AbstractMesh<2,2>* p_abstract_mesh_2; // Create an input archive ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); // Restore from the archive (*p_arch) >> p_abstract_mesh_2; MutableVertexMesh<2,2>* p_mesh_original = static_cast<MutableVertexMesh<2,2>*>(p_abstract_mesh); MutableVertexMesh<2,2>* p_mesh_loaded = static_cast<MutableVertexMesh<2,2>*>(p_abstract_mesh_2); // Test member variables were archived correctly TS_ASSERT_DELTA(p_mesh_original->GetProtorosetteFormationProbability(), 0.123, 1e-10); TS_ASSERT_DELTA(p_mesh_loaded->GetProtorosetteFormationProbability(), 0.123, 1e-10); TS_ASSERT_DELTA(p_mesh_original->GetProtorosetteResolutionProbabilityPerTimestep(), 0.234, 1e-10); TS_ASSERT_DELTA(p_mesh_loaded->GetProtorosetteResolutionProbabilityPerTimestep(), 0.234, 1e-10); TS_ASSERT_DELTA(p_mesh_original->GetRosetteResolutionProbabilityPerTimestep(), 0.345, 1e-10); TS_ASSERT_DELTA(p_mesh_loaded->GetRosetteResolutionProbabilityPerTimestep(), 0.345, 1e-10); // Compare the loaded mesh against the original TS_ASSERT_EQUALS(p_mesh_original->GetNumNodes(), 11u); TS_ASSERT_EQUALS(p_mesh_original->GetNumNodes(), p_mesh_loaded->GetNumNodes()); TS_ASSERT_EQUALS(p_mesh_original->GetNumElements(), 5u); TS_ASSERT_EQUALS(p_mesh_original->GetNumElements(), p_mesh_loaded->GetNumElements()); for (unsigned node_idx = 0 ; node_idx < p_mesh_original->GetNumNodes() ; node_idx++) { Node<2>* p_node = p_mesh_original->GetNode(node_idx); Node<2>* p_node2 = p_mesh_loaded->GetNode(node_idx); TS_ASSERT_EQUALS(p_node->IsDeleted(), p_node2->IsDeleted()); TS_ASSERT_EQUALS(p_node->GetIndex(), p_node2->GetIndex()); TS_ASSERT_EQUALS(p_node->IsBoundaryNode(), p_node2->IsBoundaryNode()); TS_ASSERT_DELTA(p_node->rGetLocation()[0], p_node2->rGetLocation()[0], 1e-10); TS_ASSERT_DELTA(p_node->rGetLocation()[1], p_node2->rGetLocation()[1], 1e-10); } for (unsigned elem_idx = 0 ; elem_idx < p_mesh_original->GetNumElements() ; elem_idx++) { TS_ASSERT_EQUALS(p_mesh_original->GetElement(elem_idx)->GetNumNodes(), p_mesh_loaded->GetElement(elem_idx)->GetNumNodes()); for (unsigned local_index=0; local_index<p_mesh_original->GetElement(elem_idx)->GetNumNodes(); local_index++) { TS_ASSERT_EQUALS(p_mesh_original->GetElement(elem_idx)->GetNodeGlobalIndex(local_index), p_mesh_loaded->GetElement(elem_idx)->GetNodeGlobalIndex(local_index)); } } // Tidy up delete p_mesh_original; delete p_mesh_loaded; } }
void TestArchivingOfPdeHandlerOnCuboid() throw(Exception) { EXIT_IF_PARALLEL; FileFinder archive_dir("archive", RelativeTo::ChasteTestOutput); std::string archive_file = "CellBasedPdeHandlerOnCuboid.arch"; ArchiveLocationInfo::SetMeshFilename("pde_handler_mesh"); { // Create a cell population HoneycombMeshGenerator generator(2, 2, 0); MutableMesh<2,2>* p_generating_mesh = generator.GetMesh(); NodesOnlyMesh<2> mesh; mesh.ConstructNodesWithoutMesh(*p_generating_mesh, 1.5); std::vector<CellPtr> cells; CellsGenerator<FixedDurationGenerationBasedCellCycleModel, 2> cells_generator; cells_generator.GenerateBasic(cells, mesh.GetNumNodes()); NodeBasedCellPopulation<2> cell_population(mesh, cells); // Create a PDE handler object using this cell population CellBasedPdeHandlerOnCuboid<2>* const p_pde_handler = new CellBasedPdeHandlerOnCuboid<2>(&cell_population); // Set member variables for testing p_pde_handler->SetWriteAverageRadialPdeSolution("averaged quantity", 5, true); p_pde_handler->SetImposeBcsOnCoarseBoundary(false); // Set up PDE and pass to handler AveragedSourcePde<2> pde(cell_population, -0.1); ConstBoundaryCondition<2> bc(1.0); PdeAndBoundaryConditions<2> pde_and_bc(&pde, &bc, false); pde_and_bc.SetDependentVariableName("averaged quantity"); p_pde_handler->AddPdeAndBc(&pde_and_bc); // Test UseCoarsePdeMesh() again ChastePoint<2> lower(0.0, 0.0); ChastePoint<2> upper(9.0, 9.0); ChasteCuboid<2> cuboid(lower, upper); p_pde_handler->UseCoarsePdeMesh(3.0, cuboid, true); // Create an output archive ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); // Archive object SimulationTime* p_simulation_time = SimulationTime::Instance(); p_simulation_time->SetEndTimeAndNumberOfTimeSteps(1.0, 11); (*p_arch) << static_cast<const SimulationTime&>(*p_simulation_time); (*p_arch) << p_pde_handler; // Tidy up SimulationTime::Destroy(); delete p_pde_handler; } { CellBasedPdeHandlerOnCuboid<2>* p_pde_handler; // Create an input archive ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); // Restore object from the archive SimulationTime* p_simulation_time = SimulationTime::Instance(); (*p_arch) >> *p_simulation_time; (*p_arch) >> p_pde_handler; // Test that the member variables were archived correctly TS_ASSERT_EQUALS(p_pde_handler->mpCellPopulation->GetNumRealCells(), 4u); TS_ASSERT_EQUALS(p_pde_handler->GetWriteAverageRadialPdeSolution(), true); TS_ASSERT_EQUALS(p_pde_handler->GetWriteDailyAverageRadialPdeSolution(), true); TS_ASSERT_EQUALS(p_pde_handler->GetImposeBcsOnCoarseBoundary(), false); TS_ASSERT_EQUALS(p_pde_handler->GetNumRadialIntervals(), 5u); TS_ASSERT_EQUALS(p_pde_handler->mAverageRadialSolutionVariableName, "averaged quantity"); ///\todo we currently do not archive mpCoarsePdeMesh - consider doing this (#1891) TS_ASSERT(p_pde_handler->GetCoarsePdeMesh() == NULL); TS_ASSERT_EQUALS(p_pde_handler->mPdeAndBcCollection.size(), 1u); TS_ASSERT_EQUALS(p_pde_handler->mPdeAndBcCollection[0]->IsNeumannBoundaryCondition(), false); // Tidy up delete p_pde_handler->mpCellPopulation; delete p_pde_handler; } }
// NB This checks that periodicity is maintained through archiving... void TestArchiving() throw (Exception) { EXIT_IF_PARALLEL; // HoneycombMeshGenerator doesn't work in parallel FileFinder archive_dir("archive", RelativeTo::ChasteTestOutput); std::string archive_file = "cylindrical_nodes_only_mesh_base.arch"; ArchiveLocationInfo::SetMeshFilename("cylindrical_nodes_only_mesh"); // Create generating mesh unsigned num_cells_across = 4; unsigned num_cells_up = 7; HoneycombMeshGenerator generator(num_cells_across,num_cells_up); TetrahedralMesh<2,2>* p_generating_mesh = generator.GetMesh(); // Convert this to a Cylindrical2dNodesOnlyMesh double periodic_width = 4.0; Cylindrical2dNodesOnlyMesh* p_mesh = new Cylindrical2dNodesOnlyMesh(periodic_width); p_mesh->ConstructNodesWithoutMesh(*p_generating_mesh, 1.0); AbstractMesh<2,2>* const p_saved_mesh = p_mesh; /* * You need the const above to stop a BOOST_STATIC_ASSERTION failure. * This is because the serialization library only allows you to save * tracked objects while the compiler considers them const, to prevent * the objects changing during the save, and so object tracking leading * to wrong results. For example, A is saved once via pointer, then * changed, then saved again. The second save notes that A was saved * before, so doesn't write its data again, and the change is lost. */ { // Serialize the mesh TS_ASSERT_DELTA((static_cast<Cylindrical2dNodesOnlyMesh*>(p_saved_mesh))->GetWidth(0), periodic_width, 1e-7); // Create output archive ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); // We have to serialize via a pointer here, or the derived class information is lost. (*p_arch) << p_saved_mesh; } { // De-serialize and compare AbstractMesh<2,2>* p_loaded_mesh; // Create an input archive ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); // Restore from the archive (*p_arch) >> p_loaded_mesh; // Compare the loaded mesh against the original Cylindrical2dNodesOnlyMesh* p_static_cast_loaded_mesh = static_cast<Cylindrical2dNodesOnlyMesh*>(p_loaded_mesh); Cylindrical2dNodesOnlyMesh* p_static_cast_saved_mesh = static_cast<Cylindrical2dNodesOnlyMesh*>(p_saved_mesh); // Compare width TS_ASSERT_DELTA(p_static_cast_loaded_mesh->GetWidth(0), periodic_width, 1e-7); TS_ASSERT_DELTA(p_static_cast_saved_mesh->GetWidth(0), periodic_width, 1e-7); // Compare nodes TS_ASSERT_EQUALS(p_static_cast_saved_mesh->GetNumNodes(), p_static_cast_loaded_mesh->GetNumNodes()); TS_ASSERT_EQUALS(p_static_cast_saved_mesh->GetNumNodes(), 28u); for (unsigned i=0; i<p_static_cast_saved_mesh->GetNumNodes(); i++) { Node<2>* p_node = p_static_cast_saved_mesh->GetNode(i); Node<2>* p_node2 = p_static_cast_loaded_mesh->GetNode(i); TS_ASSERT_EQUALS(p_node->IsDeleted(), p_node2->IsDeleted()); TS_ASSERT_EQUALS(p_node->GetIndex(), p_node2->GetIndex()); TS_ASSERT_EQUALS(p_node->IsBoundaryNode(), p_node2->IsBoundaryNode()); for (unsigned j=0; j<2; j++) { TS_ASSERT_DELTA(p_node->rGetLocation()[j], p_node2->rGetLocation()[j], 1e-4); } } // Avoid memory leak delete p_loaded_mesh; } // Avoid memory leak delete p_mesh; }
/** * 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 TestArchiving() throw(Exception) { OutputFileHandler archive_dir_("mixed_mesh_archive"); // Clear folder FileFinder main_archive_dir("mixed_mesh_archive", RelativeTo::ChasteTestOutput); std::string archive_file = "mixed_dimension_mesh.arch"; ArchiveLocationInfo::SetMeshFilename("mixed_dimension_mesh"); MixedDimensionMesh<2,2>* p_mesh = new MixedDimensionMesh<2,2>(DistributedTetrahedralMeshPartitionType::DUMB); unsigned num_nodes; unsigned local_num_nodes; unsigned num_elements; unsigned num_cable_elements; unsigned num_local_cable_elements; // archive { TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/mixed_dimension_meshes/2D_0_to_1mm_200_elements"); p_mesh->ConstructFromMeshReader(mesh_reader); num_nodes = p_mesh->GetNumNodes(); local_num_nodes = p_mesh->GetNumLocalNodes(); num_elements = p_mesh->GetNumElements(); num_cable_elements = p_mesh->GetNumCableElements(); num_local_cable_elements = p_mesh->GetNumLocalCableElements(); ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(main_archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); AbstractTetrahedralMesh<2,2>* const p_mesh_abstract = static_cast<AbstractTetrahedralMesh<2,2>* >(p_mesh); (*p_arch) << p_mesh_abstract; } FileFinder ncl_file("mixed_dimension_mesh.ncl", main_archive_dir); TS_ASSERT(ncl_file.Exists()); // restore { // Should archive the most abstract class you can to check boost knows what individual classes are. // (but here AbstractMesh doesn't have the methods below). AbstractTetrahedralMesh<2,2>* p_mesh_abstract2; // Create an input archive ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(main_archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); // restore from the archive (*p_arch) >> p_mesh_abstract2; // Check we have the right number of nodes & elements MixedDimensionMesh<2,2>* p_mesh2 = static_cast<MixedDimensionMesh<2,2>*>(p_mesh_abstract2); TS_ASSERT_EQUALS(p_mesh2->GetNumNodes(), num_nodes); TS_ASSERT_EQUALS(p_mesh2->GetNumLocalNodes(), local_num_nodes); TS_ASSERT_EQUALS(p_mesh2->GetNumElements(), num_elements); TS_ASSERT_EQUALS(p_mesh2->GetNumCableElements(), num_cable_elements); TS_ASSERT_EQUALS(p_mesh2->GetNumLocalCableElements(), num_local_cable_elements); // Check elements have the right nodes for (unsigned i=0; i<num_cable_elements; i++) { try { Element<1,2>* p_element = p_mesh->GetCableElement(i); Element<1,2>* p_element2 = p_mesh2->GetCableElement(i); TS_ASSERT_EQUALS(p_element->GetNodeGlobalIndex(0), p_element2->GetNodeGlobalIndex(0)); } catch(Exception& e) { TS_ASSERT_DIFFERS((int)e.GetShortMessage().find("does not belong to processor"),-1); } } delete p_mesh2; } // restore from a single processor archive { FileFinder archive_dir("mesh/test/data/mixed_mesh_archive", RelativeTo::ChasteSourceRoot); if ( PetscTools::IsSequential() ) { ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); AbstractTetrahedralMesh<2,2>* p_mesh_abstract3 = NULL; (*p_arch) >> p_mesh_abstract3; //Double check that the cables are intact MixedDimensionMesh<2,2>* p_mesh3 = static_cast<MixedDimensionMesh<2,2>*>(p_mesh_abstract3); Element<1,2>* p_element = p_mesh3->GetCableElement(9); TS_ASSERT_EQUALS(p_element->GetNodeGlobalIndex(0), 64u); TS_ASSERT_EQUALS(p_element->GetNodeGlobalIndex(1), 65u); TS_ASSERT_DELTA(p_element->GetAttribute(), 10.5, 1e-8); delete p_mesh_abstract3; } else { typedef ArchiveOpener<boost::archive::text_iarchive, std::ifstream> InputArchiveOpener; if (PetscTools::GetMyRank() > 0) { // Should not read this archive because none exists here. TS_ASSERT_THROWS_CONTAINS(InputArchiveOpener arch_opener(archive_dir, archive_file), "Cannot load secondary archive file:"); } else { // Should not read this archive because there are two or more processes and // this archive was written on one process. InputArchiveOpener arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); AbstractTetrahedralMesh<2,2>* p_mesh3 = NULL; TS_ASSERT_THROWS_THIS((*p_arch) >> p_mesh3, "This archive was written for a different number of processors"); } } }
// NB This checks that periodicity is maintained through archiving... void TestArchiving() throw (Exception) { FileFinder archive_dir("archive", RelativeTo::ChasteTestOutput); std::string archive_file = "cylindrical_mesh_base.arch"; ArchiveLocationInfo::SetMeshFilename("cylindrical_mesh"); // Set up a mesh unsigned cells_across = 5; unsigned cells_up = 3; double crypt_width = 5.0; unsigned thickness_of_ghost_layer = 0; CylindricalHoneycombMeshGenerator generator(cells_across, cells_up, thickness_of_ghost_layer, crypt_width/cells_across); AbstractTetrahedralMesh<2,2>* const p_mesh = generator.GetCylindricalMesh(); /* * You need the const above to stop a BOOST_STATIC_ASSERTION failure. * This is because the serialization library only allows you to save tracked * objects while the compiler considers them const, to prevent the objects * changing during the save, and so object tracking leading to wrong results. * (e.g. A is saved once via pointer, then changed, then saved again. The second * save notes that A was saved before, so doesn't write its data again, and the * change is lost.) */ { // Serialize the mesh double width = p_mesh->GetWidth(0); TS_ASSERT_DELTA(width, crypt_width, 1e-7); // Create output archive ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); // We have to serialize via a pointer here, or the derived class information is lost. (*p_arch) << p_mesh; } { // De-serialize and compare AbstractTetrahedralMesh<2,2>* p_mesh2; // Create an input archive ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); // Restore from the archive (*p_arch) >> p_mesh2; // Cylindrical2dMesh now remeshes itself on load (convert from TrianglesMeshReader to normal format) TS_ASSERT_DELTA(p_mesh2->GetWidth(0), crypt_width, 1e-7); // Compare the loaded mesh against the original TS_ASSERT_EQUALS(p_mesh->GetNumAllNodes(), p_mesh2->GetNumAllNodes()); TS_ASSERT_EQUALS(p_mesh->GetNumNodes(), p_mesh2->GetNumNodes()); TS_ASSERT_EQUALS(p_mesh->GetNumBoundaryNodes(), p_mesh2->GetNumBoundaryNodes()); for (unsigned i=0; i<p_mesh->GetNumAllNodes(); i++) { Node<2>* p_node = p_mesh->GetNode(i); Node<2>* p_node2 = p_mesh2->GetNode(i); TS_ASSERT_EQUALS(p_node->IsDeleted(), p_node2->IsDeleted()); TS_ASSERT_EQUALS(p_node->GetIndex(), p_node2->GetIndex()); TS_ASSERT_EQUALS(p_node->IsBoundaryNode(), p_node2->IsBoundaryNode()); for (unsigned j=0; j<2; j++) { TS_ASSERT_DELTA(p_node->rGetLocation()[j], p_node2->rGetLocation()[j], 1e-16); } } TS_ASSERT_EQUALS(p_mesh->GetNumElements(), p_mesh2->GetNumElements()); TS_ASSERT_EQUALS(p_mesh->GetNumAllElements(), p_mesh2->GetNumAllElements()); TS_ASSERT_EQUALS(p_mesh->GetNumBoundaryElements(), p_mesh2->GetNumBoundaryElements()); TS_ASSERT_EQUALS(p_mesh->GetNumAllBoundaryElements(), p_mesh2->GetNumAllBoundaryElements()); AbstractTetrahedralMesh<2,2>::ElementIterator iter2 = p_mesh2->GetElementIteratorBegin(); for (AbstractTetrahedralMesh<2,2>::ElementIterator iter = p_mesh->GetElementIteratorBegin(); iter != p_mesh->GetElementIteratorEnd(); ++iter, ++iter2) { TS_ASSERT_EQUALS(iter->GetNumNodes(), iter2->GetNumNodes()); for (unsigned i=0; i<iter->GetNumNodes(); i++) { TS_ASSERT_EQUALS(iter->GetNodeGlobalIndex(i), iter2->GetNodeGlobalIndex(i)); } } // We now need to free the mesh, since there is no honeycomb generator to do so. delete p_mesh2; } }
void TestArchivingCellPopulation() throw (Exception) { EXIT_IF_PARALLEL; // This test doesn't work in parallel. FileFinder archive_dir("archive", RelativeTo::ChasteTestOutput); std::string archive_file = "node_based_cell_population_with_particles.arch"; ArchiveLocationInfo::SetMeshFilename("node_based_cell_population_with_particles_mesh"); { // Need to set up time unsigned num_steps = 10; SimulationTime* p_simulation_time = SimulationTime::Instance(); p_simulation_time->SetEndTimeAndNumberOfTimeSteps(1.0, num_steps+1); // Create a simple mesh TrianglesMeshReader<2,2> mesh_reader("mesh/test/data/square_4_elements"); TetrahedralMesh<2,2> generating_mesh; generating_mesh.ConstructFromMeshReader(mesh_reader); // Convert this to a NodesOnlyMesh NodesOnlyMesh<2> mesh; mesh.ConstructNodesWithoutMesh(generating_mesh, 1.5); // Create cells std::vector<CellPtr> cells; CellsGenerator<FixedDurationGenerationBasedCellCycleModel, 2> cells_generator; cells_generator.GenerateBasic(cells, mesh.GetNumNodes()); // Create a cell population NodeBasedCellPopulationWithParticles<2>* const p_cell_population = new NodeBasedCellPopulationWithParticles<2>(mesh, cells); // Cells have been given birth times of 0, -1, -2, -3, -4. // loop over them to run to time 0.0; for (AbstractCellPopulation<2>::Iterator cell_iter = p_cell_population->Begin(); cell_iter != p_cell_population->End(); ++cell_iter) { cell_iter->ReadyToDivide(); } // Create an output archive ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); // Write the cell population to the archive (*p_arch) << static_cast<const SimulationTime&>(*p_simulation_time); (*p_arch) << p_cell_population; // Avoid memory leak SimulationTime::Destroy(); delete p_cell_population; } { // Need to set up time unsigned num_steps = 10; SimulationTime* p_simulation_time = SimulationTime::Instance(); p_simulation_time->SetStartTime(0.0); p_simulation_time->SetEndTimeAndNumberOfTimeSteps(1.0, num_steps+1); p_simulation_time->IncrementTimeOneStep(); NodeBasedCellPopulationWithParticles<2>* p_cell_population; // Restore the cell population ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); (*p_arch) >> *p_simulation_time; (*p_arch) >> p_cell_population; // Cells have been given birth times of 0, -1, -2, -3, -4. // this checks that individual cells and their models are archived. unsigned counter = 0; for (AbstractCellPopulation<2>::Iterator cell_iter = p_cell_population->Begin(); cell_iter != p_cell_population->End(); ++cell_iter) { TS_ASSERT_DELTA(cell_iter->GetAge(), (double)(counter), 1e-7); counter++; } // Check the simulation time has been restored (through the cell) TS_ASSERT_EQUALS(p_simulation_time->GetTime(), 0.0); // Check the cell population has been restored TS_ASSERT_EQUALS(p_cell_population->rGetCells().size(), 5u); TS_ASSERT_DELTA(p_cell_population->GetMechanicsCutOffLength(), 1.5, 1e-6); // Check number of nodes TS_ASSERT_EQUALS(p_cell_population->GetNumNodes(), 5u); // Check some node positions TS_ASSERT_EQUALS(p_cell_population->GetNode(3)->GetIndex(), 3u); TS_ASSERT_EQUALS(p_cell_population->GetNode(4)->GetIndex(), 4u); TS_ASSERT_DELTA(p_cell_population->GetNode(3)->rGetLocation()[0], 0.0, 1e-9); TS_ASSERT_DELTA(p_cell_population->GetNode(3)->rGetLocation()[1], 1.0, 1e-9); TS_ASSERT_DELTA(p_cell_population->GetNode(4)->rGetLocation()[0], 0.5, 1e-9); TS_ASSERT_DELTA(p_cell_population->GetNode(4)->rGetLocation()[1], 0.5, 1e-9); // Check the member variables have been restored TS_ASSERT_DELTA(p_cell_population->GetMechanicsCutOffLength(), 1.5, 1e-9); // Tidy up delete p_cell_population; } }
void TestSaveAndLoadCardiacPDE() { HeartConfig::Instance()->Reset(); // Archive settings FileFinder archive_dir("tissue_archive", RelativeTo::ChasteTestOutput); std::string archive_file = "bidomain_tissue.arch"; bool cache_replication_saved = false; double saved_printing_timestep = 2.0; double default_printing_timestep = HeartConfig::Instance()->GetPrintingTimeStep(); std::vector<cp::media_type> media_types; media_types.push_back(cp::media_type::Orthotropic); media_types.push_back(cp::media_type::Axisymmetric); media_types.push_back(cp::media_type::NoFibreOrientation); for (std::vector<cp::media_type>::iterator it = media_types.begin(); it != media_types.end(); ++it) { c_matrix<double, 3, 3> intra_tensor_before_archiving; c_matrix<double, 3, 3> extra_tensor_before_archiving; { // 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/3D_Single_tetrahedron_element", *it); TrianglesMeshReader<3,3> mesh_reader("mesh/test/data/3D_Single_tetrahedron_element"); TetrahedralMesh<3,3> mesh; mesh.ConstructFromMeshReader(mesh_reader); MyCardiacCellFactory<3> cell_factory; cell_factory.SetMesh(&mesh); BidomainTissue<3> bidomain_tissue( &cell_factory ); bidomain_tissue.SetCacheReplication(cache_replication_saved); // Not the default to check it is archived... // 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 = bidomain_tissue.rGetIntracellularConductivityTensor(0); extra_tensor_before_archiving = bidomain_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 = &bidomain_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); } { 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_bidomain_tissue; (*p_arch) >> p_bidomain_tissue; assert(p_bidomain_tissue!=NULL); const c_matrix<double, 3, 3>& intra_tensor_after_archiving = p_bidomain_tissue->rGetIntracellularConductivityTensor(0); const c_matrix<double, 3, 3>& extra_tensor_after_archiving = dynamic_cast<BidomainTissue<3>*>(p_bidomain_tissue)->rGetExtracellularConductivityTensor(0); //Naughty Gary using dynamic cast, but only for testing... 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(extra_tensor_before_archiving(i,j), extra_tensor_after_archiving(i,j), 1e-9); } } TS_ASSERT_EQUALS(cache_replication_saved, p_bidomain_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_bidomain_tissue; } } }
void TestSaveAndLoadCardiacTissue() throw (Exception) { HeartConfig::Instance()->Reset(); // Archive settings FileFinder archive_dir("monodomain_tissue_archive", RelativeTo::ChasteTestOutput); std::string archive_file = "monodomain_tissue.arch"; bool cache_replication_saved = false; double saved_printing_timestep = 2.0; double default_printing_timestep = HeartConfig::Instance()->GetPrintingTimeStep(); // Info about the first cell on this process (if any) bool has_cell = false; unsigned cell_v_index = (unsigned)(-1); double cell_v = DBL_MAX; c_matrix<double, 1, 1> tensor_before_archiving; { TrianglesMeshReader<1,1> mesh_reader("mesh/test/data/1D_0_to_1_10_elements"); TetrahedralMesh<1,1> mesh; mesh.ConstructFromMeshReader(mesh_reader); MyCardiacCellFactory cell_factory; cell_factory.SetMesh(&mesh); MonodomainTissue<1> monodomain_tissue( &cell_factory ); monodomain_tissue.SetCacheReplication(cache_replication_saved); // Not the default to check it is archived... tensor_before_archiving = monodomain_tissue.rGetIntracellularConductivityTensor(1); // Get some info about the first cell on this process (if any) const std::vector<AbstractCardiacCellInterface*>& r_cells = monodomain_tissue.rGetCellsDistributed(); has_cell = !r_cells.empty(); if (has_cell) { cell_v_index = r_cells[0]->GetVoltageIndex(); cell_v = r_cells[0]->GetVoltage(); } // 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); // Save ArchiveOpener<boost::archive::text_oarchive, std::ofstream> arch_opener(archive_dir, archive_file); boost::archive::text_oarchive* p_arch = arch_opener.GetCommonArchive(); AbstractCardiacTissue<1>* const p_archive_monodomain_tissue = &monodomain_tissue; (*p_arch) << p_archive_monodomain_tissue; HeartConfig::Reset(); TS_ASSERT_DELTA(HeartConfig::Instance()->GetPrintingTimeStep(), default_printing_timestep, 1e-9); TS_ASSERT_DIFFERS(saved_printing_timestep, default_printing_timestep); } { ArchiveOpener<boost::archive::text_iarchive, std::ifstream> arch_opener(archive_dir, archive_file); boost::archive::text_iarchive* p_arch = arch_opener.GetCommonArchive(); AbstractCardiacTissue<1>* p_monodomain_tissue; (*p_arch) >> p_monodomain_tissue; // Test rGetIntracellularConductivityTensor const c_matrix<double, 1, 1>& tensor_after_archiving = p_monodomain_tissue->rGetIntracellularConductivityTensor(1); TS_ASSERT_DELTA(tensor_before_archiving(0,0), tensor_after_archiving(0,0), 1e-9); TS_ASSERT_EQUALS(cache_replication_saved, p_monodomain_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 // Test cardiac cells have also been archived const std::vector<AbstractCardiacCellInterface*>& r_cells = p_monodomain_tissue->rGetCellsDistributed(); TS_ASSERT_EQUALS(has_cell, !r_cells.empty()); if (has_cell) { TS_ASSERT_EQUALS(cell_v_index, r_cells[0]->GetVoltageIndex()); TS_ASSERT_EQUALS(cell_v, r_cells[0]->GetVoltage()); } delete p_monodomain_tissue; } }