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);
		}
	}
}
Example #3
0
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);
}
Example #4
0
    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;
}
Example #10
0
    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;
        }
    }
Example #22
0
    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;
        }
    }