AbstractCardiacTissue.hpp

00001 /*
00002 
00003 Copyright (c) 2005-2015, University of Oxford.
00004 All rights reserved.
00005 
00006 University of Oxford means the Chancellor, Masters and Scholars of the
00007 University of Oxford, having an administrative office at Wellington
00008 Square, Oxford OX1 2JD, UK.
00009 
00010 This file is part of Chaste.
00011 
00012 Redistribution and use in source and binary forms, with or without
00013 modification, are permitted provided that the following conditions are met:
00014  * Redistributions of source code must retain the above copyright notice,
00015    this list of conditions and the following disclaimer.
00016  * Redistributions in binary form must reproduce the above copyright notice,
00017    this list of conditions and the following disclaimer in the documentation
00018    and/or other materials provided with the distribution.
00019  * Neither the name of the University of Oxford nor the names of its
00020    contributors may be used to endorse or promote products derived from this
00021    software without specific prior written permission.
00022 
00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00024 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00025 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00026 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 
00034 */
00035 #ifndef ABSTRACTCARDIACTISSUE_HPP_
00036 #define ABSTRACTCARDIACTISSUE_HPP_
00037 
00038 #include <set>
00039 #include <vector>
00040 #include <boost/shared_ptr.hpp>
00041 
00042 #include "UblasMatrixInclude.hpp"
00043 
00044 #include "ChasteSerialization.hpp"
00045 #include "ClassIsAbstract.hpp"
00046 #include "ChasteSerializationVersion.hpp"
00047 #include <boost/serialization/base_object.hpp>
00048 #include <boost/serialization/shared_ptr.hpp>
00049 #include <boost/serialization/vector.hpp>
00050 #include <boost/serialization/string.hpp>
00051 #include <boost/serialization/split_member.hpp>
00052 
00053 #include "AbstractCardiacCellInterface.hpp"
00054 #include "FakeBathCell.hpp"
00055 #include "AbstractCardiacCellFactory.hpp"
00056 #include "AbstractConductivityTensors.hpp"
00057 #include "AbstractPurkinjeCellFactory.hpp"
00058 #include "ReplicatableVector.hpp"
00059 #include "HeartConfig.hpp"
00060 #include "ArchiveLocationInfo.hpp"
00061 #include "AbstractDynamicallyLoadableEntity.hpp"
00062 #include "DynamicModelLoaderRegistry.hpp"
00063 #include "AbstractConductivityModifier.hpp"
00064 
00077 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM = ELEMENT_DIM>
00078 class AbstractCardiacTissue : private boost::noncopyable
00079 {
00080 private:
00081 
00083     friend class boost::serialization::access;
00084     friend class TestMonodomainTissue;
00085 
00092     template<class Archive>
00093     void save(Archive & archive, const unsigned int version) const
00094     {
00095         if (version >= 3)
00096         {
00097             archive & mHasPurkinje;
00098         }
00099         if (version >= 2)
00100         {
00101             archive & mExchangeHalos;
00102         }
00103         // Don't use the std::vector serialization for cardiac cells, so that we can load them
00104         // more cleverly when migrating checkpoints.
00105         SaveCardiacCells(*ProcessSpecificArchive<Archive>::Get(), version);
00106 
00107         // archive & mpMesh; Archived in save/load_constructs at the bottom of Mono/BidomainTissue.hpp
00108         // archive & mpIntracellularConductivityTensors; Loaded from HeartConfig every time constructor is called
00109         if (HeartConfig::Instance()->IsMeshProvided() && HeartConfig::Instance()->GetLoadMesh())
00110         {
00111             switch (HeartConfig::Instance()->GetConductivityMedia())
00112             {
00113                 case cp::media_type::Orthotropic:
00114                 {
00115                     FileFinder source_file(mFibreFilePathNoExtension + ".ortho", RelativeTo::AbsoluteOrCwd);
00116                     assert(source_file.Exists());
00117                     FileFinder dest_file(ArchiveLocationInfo::GetArchiveRelativePath() + ArchiveLocationInfo::GetMeshFilename() + ".ortho", RelativeTo::ChasteTestOutput);
00118 
00119                     TRY_IF_MASTER(source_file.CopyTo(dest_file));
00120                     break;
00121                 }
00122 
00123                 case cp::media_type::Axisymmetric:
00124                 {
00125                     FileFinder source_file(mFibreFilePathNoExtension + ".axi", RelativeTo::AbsoluteOrCwd);
00126                     assert(source_file.Exists());
00127                     FileFinder dest_file(ArchiveLocationInfo::GetArchiveRelativePath()
00128                                        + ArchiveLocationInfo::GetMeshFilename() + ".axi", RelativeTo::ChasteTestOutput);
00129 
00130                     TRY_IF_MASTER(source_file.CopyTo(dest_file));
00131                     break;
00132                 }
00133 
00134                 case cp::media_type::NoFibreOrientation:
00135                     break;
00136 
00137                 default :
00138                     NEVER_REACHED;
00139             }
00140         }
00141 
00142         // archive & mIionicCacheReplicated; // will be regenerated
00143         // archive & mIntracellularStimulusCacheReplicated; // will be regenerated
00144         archive & mDoCacheReplication;
00145         // archive & mMeshUnarchived; Not archived since set to true when archiving constructor is called.
00146 
00147         (*ProcessSpecificArchive<Archive>::Get()) & mpDistributedVectorFactory;
00148 
00149         // Paranoia: check we agree with the mesh on who owns what
00150         assert(mpDistributedVectorFactory == mpMesh->GetDistributedVectorFactory());
00151         assert(mpDistributedVectorFactory->GetLow()==mpMesh->GetDistributedVectorFactory()->GetLow());
00152         assert(mpDistributedVectorFactory->GetLocalOwnership()==mpMesh->GetDistributedVectorFactory()->GetLocalOwnership());
00153     }
00154 
00161     template<class Archive>
00162     void load(Archive & archive, const unsigned int version)
00163     {
00164         // archive & mpMesh; Archived in save/load_constructs at the bottom of Mono/BidomainTissue.hpp
00165         // archive & mpIntracellularConductivityTensors; Loaded from HeartConfig every time constructor is called
00166 
00167         if (version >= 3)
00168         {
00169             archive & mHasPurkinje;
00170             if (mHasPurkinje)
00171             {
00172                 mPurkinjeIionicCacheReplicated.Resize(mpDistributedVectorFactory->GetProblemSize());
00173             }
00174         }
00175         if (version >= 2)
00176         {
00177             archive & mExchangeHalos;
00178             if (mExchangeHalos)
00179             {
00180                 mpMesh->CalculateNodeExchange(mNodesToSendPerProcess, mNodesToReceivePerProcess);
00181                 CalculateHaloNodesFromNodeExchange();
00182                 unsigned num_halo_nodes = mHaloNodes.size();
00183                 mHaloCellsDistributed.resize( num_halo_nodes );
00184                 for (unsigned local_index = 0; local_index < num_halo_nodes; local_index++)
00185                 {
00186                     unsigned global_index = mHaloNodes[local_index];
00187                     mHaloGlobalToLocalIndexMap[global_index] = local_index;
00188                 }
00189             }
00190         }
00191 
00192         // mCellsDistributed & mHaloCellsDistributed:
00193         LoadCardiacCells(*ProcessSpecificArchive<Archive>::Get(), version);
00194 
00195         // archive & mIionicCacheReplicated; // will be regenerated
00196         // archive & mIntracellularStimulusCacheReplicated; // will be regenerated
00197         archive & mDoCacheReplication;
00198 
00199         // we no longer have a bool mDoOneCacheReplication, but to maintain backwards compatibility
00200         // we archive something if version==0
00201         if (version==0)
00202         {
00203             bool do_one_cache_replication = true;
00204             archive & do_one_cache_replication;
00205         }
00206 
00207         (*ProcessSpecificArchive<Archive>::Get()) & mpDistributedVectorFactory;
00208 
00209         // Paranoia: check we agree with the mesh on who owns what
00210         assert(mpDistributedVectorFactory == mpMesh->GetDistributedVectorFactory());
00211         assert(mpDistributedVectorFactory->GetLow()==mpMesh->GetDistributedVectorFactory()->GetLow());
00212         assert(mpDistributedVectorFactory->GetLocalOwnership()==mpMesh->GetDistributedVectorFactory()->GetLocalOwnership());
00213         // archive & mMeshUnarchived; Not archived since set to true when archiving constructor is called.
00214 
00215         // not archiving mpConductivityModifier for the time being (mechanics simulations are only use-case at the moment, and they
00216         // do not get archived...). mpConductivityModifier has to be reset to NULL upon load.
00217         mpConductivityModifier = NULL;
00218     }
00219     BOOST_SERIALIZATION_SPLIT_MEMBER()
00220 
00221     
00224     void CreateIntracellularConductivityTensor();
00225 
00226 protected:
00227 
00229     AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>* mpMesh;
00230 
00233     AbstractConductivityTensors<ELEMENT_DIM,SPACE_DIM>* mpIntracellularConductivityTensors;
00234 
00236     std::vector< AbstractCardiacCellInterface* > mCellsDistributed;
00237 
00239     std::vector< AbstractCardiacCellInterface* > mPurkinjeCellsDistributed;
00240 
00245     ReplicatableVector mIionicCacheReplicated;
00246 
00251     ReplicatableVector mPurkinjeIionicCacheReplicated;
00252 
00257     ReplicatableVector mIntracellularStimulusCacheReplicated;
00258 
00263     ReplicatableVector mPurkinjeIntracellularStimulusCacheReplicated;
00264 
00266     HeartConfig* mpConfig;
00267 
00276     DistributedVectorFactory* mpDistributedVectorFactory;
00277 
00281     std::string mFibreFilePathNoExtension;
00282 
00288     AbstractConductivityModifier<ELEMENT_DIM,SPACE_DIM>* mpConductivityModifier;
00289 
00291     bool mHasPurkinje;
00292 
00302     bool mDoCacheReplication;
00303 
00307     bool mMeshUnarchived;
00308 
00313     bool mExchangeHalos;
00314 
00316     std::vector<unsigned> mHaloNodes;
00317 
00319     std::vector< AbstractCardiacCellInterface* > mHaloCellsDistributed;
00320 
00322     std::map<unsigned, unsigned> mHaloGlobalToLocalIndexMap;
00323 
00329     std::vector<std::vector<unsigned> > mNodesToSendPerProcess;
00330 
00335     std::vector<std::vector<unsigned> > mNodesToReceivePerProcess;
00336 
00342     void CalculateHaloNodesFromNodeExchange();
00343 
00350     void SetUpHaloCells(AbstractCardiacCellFactory<ELEMENT_DIM,SPACE_DIM>* pCellFactory);
00351 
00352 public:
00363     AbstractCardiacTissue(AbstractCardiacCellFactory<ELEMENT_DIM,SPACE_DIM>* pCellFactory, bool exchangeHalos=false);
00364 
00370     AbstractCardiacTissue(AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>* pMesh);
00371 
00373     virtual ~AbstractCardiacTissue();
00374 
00376     bool HasPurkinje();
00377 
00384     void SetCacheReplication(bool doCacheReplication);
00385 
00391     bool GetDoCacheReplication();
00392 
00396     const c_matrix<double, SPACE_DIM, SPACE_DIM>& rGetIntracellularConductivityTensor(unsigned elementIndex);
00397 
00405     virtual const c_matrix<double, SPACE_DIM, SPACE_DIM>& rGetExtracellularConductivityTensor(unsigned elementIndex);
00406 
00415     AbstractCardiacCellInterface* GetCardiacCell( unsigned globalIndex );
00416 
00425     AbstractCardiacCellInterface* GetPurkinjeCell( unsigned globalIndex );
00426 
00435     AbstractCardiacCellInterface* GetCardiacCellOrHaloCell( unsigned globalIndex );
00436 
00446     virtual void SolveCellSystems(Vec existingSolution, double time, double nextTime, bool updateVoltage=false);
00447 
00449     ReplicatableVector& rGetIionicCacheReplicated();
00450 
00452     ReplicatableVector& rGetIntracellularStimulusCacheReplicated();
00453 
00455     ReplicatableVector& rGetPurkinjeIionicCacheReplicated();
00456 
00458     ReplicatableVector& rGetPurkinjeIntracellularStimulusCacheReplicated();
00459 
00460 
00468     void UpdateCaches(unsigned globalIndex, unsigned localIndex, double nextTime);
00469 
00477     void UpdatePurkinjeCaches(unsigned globalIndex, unsigned localIndex, double nextTime);
00478 
00482     void ReplicateCaches();
00483 
00487     const std::vector<AbstractCardiacCellInterface*>& rGetCellsDistributed() const;
00488 
00492     const std::vector<AbstractCardiacCellInterface*>& rGetPurkinjeCellsDistributed() const;
00493 
00499     const AbstractTetrahedralMesh<ELEMENT_DIM,SPACE_DIM>* pGetMesh() const;
00500 
00506     void SetConductivityModifier(AbstractConductivityModifier<ELEMENT_DIM,SPACE_DIM>* pModifier);
00507 
00519     template<class Archive>
00520     void SaveCardiacCells(Archive & archive, const unsigned int version) const
00521     {
00522         const std::vector<AbstractCardiacCellInterface*> & r_cells_distributed = rGetCellsDistributed();
00523         assert(mpDistributedVectorFactory == this->mpMesh->GetDistributedVectorFactory());
00524         archive & mpDistributedVectorFactory; // Needed when loading
00525         const unsigned num_cells = r_cells_distributed.size();
00526         archive & num_cells;
00527         for (unsigned i=0; i<num_cells; i++)
00528         {
00529             AbstractDynamicallyLoadableEntity* p_entity = dynamic_cast<AbstractDynamicallyLoadableEntity*>(r_cells_distributed[i]);
00530             bool is_dynamic = (p_entity != NULL);
00531             archive & is_dynamic;
00532             if (is_dynamic)
00533             {
00534 #ifdef CHASTE_CAN_CHECKPOINT_DLLS
00535                 archive & p_entity->GetLoader()->GetLoadableModulePath();
00536 #else
00537                 // We should have thrown an exception before this point
00538                 NEVER_REACHED;
00539 #endif // CHASTE_CAN_CHECKPOINT_DLLS
00540             }
00541             archive & r_cells_distributed[i];
00542             if (mHasPurkinje)
00543             {
00544                 archive & rGetPurkinjeCellsDistributed()[i];
00545             }
00546         }
00547     }
00548 
00561     template<class Archive>
00562     void LoadCardiacCells(Archive & archive, const unsigned int version)
00563     {
00564         // Note that p_factory loaded from this archive might not be the same as our mesh's factory,
00565         // since we're loading a process-specific archive that could have been written by any process.
00566         // We therefore need to use p_mesh_factory to determine the partitioning in use for the resumed
00567         // simulation, and p_factory to determine what the original partitioning was when the simulation
00568         // was saved.
00569         DistributedVectorFactory* p_factory;
00570         DistributedVectorFactory* p_mesh_factory = this->mpMesh->GetDistributedVectorFactory();
00571         archive & p_factory;
00572         unsigned num_cells;
00573         archive & num_cells;
00574         if (mCellsDistributed.empty())
00575         {
00576             mCellsDistributed.resize(p_mesh_factory->GetLocalOwnership());
00577 #ifndef NDEBUG
00578             // Paranoia
00579             for (unsigned i=0; i<mCellsDistributed.size(); i++)
00580             {
00581                 assert(mCellsDistributed[i] == NULL);
00582             }
00583 #endif
00584         }
00585         else
00586         {
00587             assert(mCellsDistributed.size() == p_mesh_factory->GetLocalOwnership());
00588         }
00589         if (mHasPurkinje)
00590         {
00591             if (mPurkinjeCellsDistributed.empty())
00592             {
00593                 mPurkinjeCellsDistributed.resize(p_mesh_factory->GetLocalOwnership());
00594             }
00595             else
00596             {
00597                 assert(mPurkinjeCellsDistributed.size() == p_mesh_factory->GetLocalOwnership());
00598             }
00599         }
00600 
00601         // We don't store a cell index in the archive, so need to work out what global index this tissue starts at.
00602         // If we have an original factory we use the original low index; otherwise we use the current low index.
00603         unsigned index_low = p_factory->GetOriginalFactory() ? p_factory->GetOriginalFactory()->GetLow() : p_mesh_factory->GetLow();
00604 
00605         // Track fake cells (which might have multiple pointers to the same object) to make sure we only delete non-local ones
00606         std::set<FakeBathCell*> fake_cells_non_local, fake_cells_local;
00607 
00608         /*
00609          * Historical note:
00610          *
00611          * We always do a dumb partition when we unarchive.
00612          *
00613          * When unarchive was first implemented in parallel (migration #1199) it was thought that we might want to repartition the mesh. This would be feasible and would give
00614          * better partitions when we move to different numbers of processes.  However it would require us to apply a new permutation to entire data structure.
00615          *
00616          * In the case where the original mesh was permuted and *copied* into the archive, we need to apply the stored permutation to the mesh but not to the archive (cells).  That
00617          * is, any permutation stored with the mesh can be safely ignored.  (If we also had to repartition/permute the archive, then we would be applying a double permutation to the
00618          * mesh and a single permutation to archive.)
00619          *
00620          */
00621         for (unsigned local_index=0; local_index<num_cells; local_index++)
00622         {
00623             // Figure out where this cell goes
00624             unsigned global_index = index_low + local_index;
00625             bool local = p_mesh_factory->IsGlobalIndexLocal(global_index);
00626 
00627             // Check if this will be a halo cell
00628             std::map<unsigned, unsigned>::const_iterator halo_position;
00629             bool halo = ((halo_position=mHaloGlobalToLocalIndexMap.find(global_index)) != mHaloGlobalToLocalIndexMap.end());
00630             // halo_position->second is local halo index
00631 
00632             bool is_dynamic;
00633             archive & is_dynamic;
00634             if (is_dynamic)
00635             {
00636 #ifdef CHASTE_CAN_CHECKPOINT_DLLS
00637                 // Ensure the shared object file for this cell model is loaded.
00638                 // We need to do this here, rather than in the class' serialization code,
00639                 // because that code won't be available until this is done...
00640                 std::string shared_object_path;
00641                 archive & shared_object_path;
00642                 DynamicModelLoaderRegistry::Instance()->GetLoader(shared_object_path);
00643 #else
00644                 // Since checkpoints with dynamically loadable cells can only be
00645                 // created on Boost>=1.37, trying to load such a checkpoint on an
00646                 // earlier Boost would give an error when first opening the archive.
00647                 NEVER_REACHED;
00648 #endif // CHASTE_CAN_CHECKPOINT_DLLS
00649             }
00650             AbstractCardiacCellInterface* p_cell;
00651             archive & p_cell;
00652             AbstractCardiacCellInterface* p_purkinje_cell = NULL;
00653             if (mHasPurkinje)
00654             {
00655                 archive & p_purkinje_cell;
00656             }
00657             // Check if it's a fake cell
00658             FakeBathCell* p_fake = dynamic_cast<FakeBathCell*>(p_cell);
00659             if (p_fake)
00660             {
00661                 if (halo || local)
00662                 {
00663                     fake_cells_local.insert(p_fake);
00664                 }
00665                 else
00666                 {
00667                     fake_cells_non_local.insert(p_fake);
00668                 }
00669             }
00670             FakeBathCell* p_fake_purkinje = dynamic_cast<FakeBathCell*>(p_purkinje_cell);
00671             if (p_fake_purkinje)
00672             {
00673                 if (halo || local)
00674                 {
00675                     fake_cells_local.insert(p_fake_purkinje);
00676                 }
00677                 else
00678                 {
00679                     fake_cells_non_local.insert(p_fake_purkinje);
00680                 }
00681             }
00682             // Add real cells to the local or halo vectors
00683             if (local)
00684             {
00685                 // Note that the original local_index was relative to the archived partition (distributed vector)
00686                 // The new_local_index is local relative to the new partition in memory
00687                 unsigned new_local_index = global_index - p_mesh_factory->GetLow();
00688                 assert(mCellsDistributed[new_local_index] == NULL);
00689                 mCellsDistributed[new_local_index] = p_cell;
00690                 if (mHasPurkinje)
00691                 {
00692                     assert(mPurkinjeCellsDistributed[new_local_index] == NULL);
00693                     mPurkinjeCellsDistributed[new_local_index] = p_purkinje_cell;
00694                 }
00695             }
00696             else if (halo)
00697             {
00698                 assert(mHaloCellsDistributed[halo_position->second] == NULL);
00699                 mHaloCellsDistributed[halo_position->second] = p_cell;
00700             }
00701             else
00702             {
00703                 if (!p_fake)
00704                 {
00705                     // Non-local real cell, so free the memory.
00706                     delete p_cell;
00707                 }
00708                 if (!p_fake_purkinje)
00709                 {
00710                     // This will be NULL if there's no Purkinje, so a delete is OK.
00711                     delete p_purkinje_cell;
00712                 }
00713             }
00714         }
00715 
00716         // Delete any unused fake cells
00717         for (std::set<FakeBathCell*>::iterator it = fake_cells_non_local.begin();
00718              it != fake_cells_non_local.end();
00719              ++it)
00720         {
00721             if (fake_cells_local.find(*it) == fake_cells_local.end())
00722             {
00723                 delete (*it);
00724             }
00725         }
00726     }
00727 };
00728 
00729 TEMPLATED_CLASS_IS_ABSTRACT_2_UNSIGNED(AbstractCardiacTissue)
00730 
00731 namespace boost {
00732 namespace serialization {
00739 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00740 struct version<AbstractCardiacTissue<ELEMENT_DIM, SPACE_DIM> >
00741 {
00743     CHASTE_VERSION_CONTENT(3);
00744 };
00745 } // namespace serialization
00746 } // namespace boost
00747 
00748 #endif /*ABSTRACTCARDIACTISSUE_HPP_*/
00749 

Generated by  doxygen 1.6.2