Chaste Release::3.1
HeartConfigRelatedCellFactory.cpp
00001 /*
00002 
00003 Copyright (c) 2005-2012, 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 
00036 #include "HeartConfigRelatedCellFactory.hpp"
00037 
00038 #include <sstream>
00039 #include "HeartGeometryInformation.hpp"
00040 #include "ChasteNodesList.hpp"
00041 #include "HeartFileFinder.hpp"
00042 #include "CellMLToSharedLibraryConverter.hpp"
00043 #include "AbstractCardiacCellInterface.hpp"
00044 #include "Warnings.hpp"
00045 // This is needed to prevent the chaste_libs=0 build failing
00046 // on tests that use a dynamically loaded CVODE model
00047 #include "AbstractCvodeCell.hpp"
00048 
00049 template<unsigned SPACE_DIM>
00050 HeartConfigRelatedCellFactory<SPACE_DIM>::HeartConfigRelatedCellFactory()
00051     : AbstractCardiacCellFactory<SPACE_DIM>(),
00052       mDefaultIonicModel(HeartConfig::Instance()->GetDefaultIonicModel())
00053 {
00054     // Read and store possible region definitions
00055     HeartConfig::Instance()->GetIonicModelRegions(mIonicModelRegions,
00056                                                   mIonicModelsDefined);
00057 
00058     // Read and store Stimuli
00059     HeartConfig::Instance()->GetStimuli(mStimuliApplied, mStimulatedAreas);
00060 
00061     // if no stimuli provided in XML, need electrodes instead
00062     if (mStimuliApplied.size()==0  &&  (HeartConfig::Instance()->IsElectrodesPresent() == false) )
00063     {
00064          EXCEPTION("Simulation needs a stimulus (either <Stimuli> or <Electrodes>).");
00065     }
00066 
00067     // Read and store Cell Heterogeneities
00068     HeartConfig::Instance()->GetCellHeterogeneities(mCellHeterogeneityAreas,
00069                                                     mScaleFactorGks,
00070                                                     mScaleFactorIto,
00071                                                     mScaleFactorGkr,
00072                                                     &mParameterSettings);
00073 
00074     // Do we need to convert any CellML files?
00075     PreconvertCellmlFiles();
00076 }
00077 
00078 template<unsigned SPACE_DIM>
00079 void HeartConfigRelatedCellFactory<SPACE_DIM>::PreconvertCellmlFiles()
00080 {
00081     if (mDefaultIonicModel.Dynamic().present())
00082     {
00083         LoadDynamicModel(mDefaultIonicModel, true);
00084     }
00085     for (unsigned i=0; i<mIonicModelsDefined.size(); i++)
00086     {
00087         if (mIonicModelsDefined[i].Dynamic().present())
00088         {
00089             LoadDynamicModel(mIonicModelsDefined[i], true);
00090         }
00091     }
00092 }
00093 
00094 template<unsigned SPACE_DIM>
00095 DynamicCellModelLoaderPtr HeartConfigRelatedCellFactory<SPACE_DIM>::LoadDynamicModel(
00096         const cp::ionic_model_selection_type& rModel,
00097         bool isCollective)
00098 {
00099     assert(rModel.Dynamic().present());
00100     HeartFileFinder file_finder(rModel.Dynamic()->Path());
00101     CellMLToSharedLibraryConverter converter;
00102     return converter.Convert(file_finder, isCollective);
00103 }
00104 
00105 template<unsigned SPACE_DIM>
00106 HeartConfigRelatedCellFactory<SPACE_DIM>::~HeartConfigRelatedCellFactory()
00107 {
00108 }
00109 
00110 template<unsigned SPACE_DIM>
00111 AbstractCardiacCellInterface* HeartConfigRelatedCellFactory<SPACE_DIM>::CreateCellWithIntracellularStimulus(
00112         boost::shared_ptr<AbstractStimulusFunction> intracellularStimulus,
00113         unsigned nodeIndex)
00114 {
00115     cp::ionic_model_selection_type ionic_model = mDefaultIonicModel;
00116 
00117     for (unsigned ionic_model_region_index = 0;
00118          ionic_model_region_index < mIonicModelRegions.size();
00119          ++ionic_model_region_index)
00120     {
00121         if ( mIonicModelRegions[ionic_model_region_index]->DoesContain(this->GetMesh()->GetNode(nodeIndex)->GetPoint()) )
00122         {
00123             ionic_model = mIonicModelsDefined[ionic_model_region_index];
00124             break;
00125         }
00126     }
00127 
00128     AbstractCardiacCellInterface* p_cell = NULL;
00129 
00130     if (ionic_model.Dynamic().present())
00131     {
00132 #ifndef CHASTE_CAN_CHECKPOINT_DLLS
00133         if (HeartConfig::Instance()->GetCheckpointSimulation())
00134         {
00135             EXCEPTION("Checkpointing is not compatible with dynamically loaded cell models on Boost<1.37.");
00136         }
00137 #endif // CHASTE_CAN_CHECKPOINT_DLLS
00138         // Load model from shared library
00139         DynamicCellModelLoaderPtr p_loader = LoadDynamicModel(ionic_model, false);
00140         p_cell = p_loader->CreateCell(this->mpSolver, intracellularStimulus);
00141         if (!dynamic_cast<AbstractCardiacCell*>(p_cell))
00142         {
00143             delete p_cell;
00144             EXCEPTION("CVODE cannot be used as a cell model solver in tissue simulations: do not use the --cvode flag.");
00145         }
00146     }
00147     else
00148     {
00149         assert(ionic_model.Hardcoded().present());
00150         switch(ionic_model.Hardcoded().get())
00151         {
00152             case(cp::ionic_models_available_type::LuoRudyI):
00153             {
00154                 p_cell = new CellLuoRudy1991FromCellML(this->mpSolver, intracellularStimulus);
00155                 break;
00156             }
00157 
00158             case(cp::ionic_models_available_type::LuoRudyIBackwardEuler):
00159             {
00160                 p_cell = new CellLuoRudy1991FromCellMLBackwardEuler(this->mpSolver, intracellularStimulus);
00161                 break;
00162             }
00163 
00164             case(cp::ionic_models_available_type::Fox2002):
00165             {
00166                 p_cell = new CellFoxModel2002FromCellML(this->mpSolver, intracellularStimulus);
00167                 break;
00168             }
00169 
00170             case(cp::ionic_models_available_type::Fox2002BackwardEuler):
00171             {
00172                 p_cell = new CellFoxModel2002FromCellMLBackwardEuler(this->mpSolver, intracellularStimulus);
00173                 break;
00174             }
00175 
00176             case(cp::ionic_models_available_type::DifrancescoNoble):
00177             {
00178                 p_cell = new CellDiFrancescoNoble1985FromCellML(this->mpSolver, intracellularStimulus);
00179                 break;
00180             }
00181 
00182             case(cp::ionic_models_available_type::MahajanShiferaw):
00183             {
00184                 p_cell = new CellMahajan2008FromCellML(this->mpSolver, intracellularStimulus);
00185                 break;
00186             }
00187 
00188             case(cp::ionic_models_available_type::MahajanShiferawBackwardEuler):
00189             {
00190                 p_cell = new CellMahajan2008FromCellMLBackwardEuler(this->mpSolver, intracellularStimulus);
00191                 break;
00192             }
00193 
00194             case(cp::ionic_models_available_type::tenTusscher2006):
00195             {
00196                 p_cell = new CellTenTusscher2006EpiFromCellML(this->mpSolver, intracellularStimulus);
00197                 break;
00198             }
00199 
00200             case(cp::ionic_models_available_type::tenTusscher2006BackwardEuler):
00201             {
00202                 p_cell = new CellTenTusscher2006EpiFromCellMLBackwardEuler(this->mpSolver, intracellularStimulus);
00203                 break;
00204             }
00205 
00206             case(cp::ionic_models_available_type::Maleckar):
00207             {
00208                 p_cell = new CellMaleckar2008FromCellML(this->mpSolver, intracellularStimulus);
00209                 break;
00210             }
00211 
00212             case(cp::ionic_models_available_type::HodgkinHuxley):
00213             {
00214                 p_cell = new CellHodgkinHuxley1952FromCellML(this->mpSolver, intracellularStimulus);
00215                 break;
00216             }
00217 
00218             case(cp::ionic_models_available_type::FaberRudy2000):
00219             {
00220                 p_cell = new CellFaberRudy2000FromCellML(this->mpSolver, intracellularStimulus);
00221                 break;
00222             }
00223 
00224             case(cp::ionic_models_available_type::FaberRudy2000Optimised):
00225             {
00226                 p_cell = new CellFaberRudy2000FromCellMLOpt(this->mpSolver, intracellularStimulus);
00227                 break;
00228             }
00229 
00230             default:
00231             {
00232                //If the ionic model is not in the current enumeration then the XML parser will have picked it up before now!
00233                NEVER_REACHED;
00234             }
00235         }
00236     }
00237 
00238     // Set parameters
00239     try
00240     {
00241         SetCellParameters(p_cell, nodeIndex);
00242     }
00243     catch (const Exception& e)
00244     {
00245         delete p_cell;
00246         throw e;
00247     }
00248     // Generate lookup tables if present
00249     p_cell->GetLookupTableCollection();
00250 
00251     return p_cell;
00252 }
00253 
00254 
00255 template<unsigned SPACE_DIM>
00256 void HeartConfigRelatedCellFactory<SPACE_DIM>::SetCellParameters(AbstractCardiacCellInterface* pCell,
00257                                                                  unsigned nodeIndex)
00258 {
00259     // Special case for backwards-compatibility: scale factors
00260     for (unsigned ht_index = 0;
00261          ht_index < mCellHeterogeneityAreas.size();
00262          ++ht_index)
00263     {
00264         if ( mCellHeterogeneityAreas[ht_index]->DoesContain(this->GetMesh()->GetNode(nodeIndex)->GetPoint()) )
00265         {
00266             try
00267             {
00268                 pCell->SetParameter("ScaleFactorGks", mScaleFactorGks[ht_index]);
00269                 pCell->SetParameter("ScaleFactorGkr", mScaleFactorGkr[ht_index]);
00270                 pCell->SetParameter("ScaleFactorIto", mScaleFactorIto[ht_index]);
00271             }
00272             catch (const Exception& e)
00273             {
00274                 // Just ignore missing parameter errors in this case
00275             }
00276         }
00277     }
00278 
00280     if (HeartConfig::Instance()->HasDrugDose())
00281     {
00282         double drug_dose = HeartConfig::Instance()->GetDrugDose();
00283         std::map<std::string, std::pair<double, double> > ic50_values = HeartConfig::Instance()->GetIc50Values();
00284         for (std::map<std::string, std::pair<double, double> >::iterator it = ic50_values.begin();
00285              it != ic50_values.end();
00286              ++it)
00287         {
00288             const std::string param_name = it->first + "_conductance";
00289             if (dynamic_cast<AbstractUntemplatedParameterisedSystem*>(pCell)->HasParameter(param_name))
00290             {
00291                 const double original_conductance = pCell->GetParameter(param_name);
00292                 const double ic50 = it->second.first;
00293                 const double hill = it->second.second;
00294                 const double new_conductance = original_conductance/(1.0 + pow(drug_dose/ic50, hill));
00295                 pCell->SetParameter(param_name, new_conductance);
00296             }
00297             else
00298             {
00299                 WARNING("Cannot apply drug to cell at node " << nodeIndex << " as it has no parameter named '" << param_name << "'.");
00300             }
00301         }
00302     }
00303 
00304     // SetParameter elements go next so they override the old ScaleFactor* elements.
00305     for (unsigned ht_index = 0;
00306          ht_index < mCellHeterogeneityAreas.size();
00307          ++ht_index)
00308     {
00309         if ( mCellHeterogeneityAreas[ht_index]->DoesContain(this->GetMesh()->GetNode(nodeIndex)->GetPoint()) )
00310         {
00311             for (std::map<std::string, double>::iterator param_it = mParameterSettings[ht_index].begin();
00312                  param_it != mParameterSettings[ht_index].end();
00313                  ++param_it)
00314             {
00315                 pCell->SetParameter(param_it->first, param_it->second);
00316             }
00317         }
00318     }
00319 }
00320 
00321 
00322 template<unsigned SPACE_DIM>
00323 void HeartConfigRelatedCellFactory<SPACE_DIM>::SetCellIntracellularStimulus(AbstractCardiacCellInterface* pCell,
00324                                                                             unsigned nodeIndex)
00325 {
00326     boost::shared_ptr<MultiStimulus> node_specific_stimulus(new MultiStimulus());
00327     // Check which of the defined stimuli contain the current node
00328     for (unsigned stimulus_index = 0;
00329          stimulus_index < mStimuliApplied.size();
00330          ++stimulus_index)
00331     {
00332         if ( mStimulatedAreas[stimulus_index]->DoesContain(this->GetMesh()->GetNode(nodeIndex)->GetPoint()) )
00333         {
00334             node_specific_stimulus->AddStimulus(mStimuliApplied[stimulus_index]);
00335         }
00336     }
00337     pCell->SetIntracellularStimulusFunction(node_specific_stimulus);
00338 }
00339 
00340 
00341 template<unsigned SPACE_DIM>
00342 AbstractCardiacCellInterface* HeartConfigRelatedCellFactory<SPACE_DIM>::CreateCardiacCellForTissueNode(unsigned nodeIndex)
00343 {
00344     boost::shared_ptr<MultiStimulus> node_specific_stimulus(new MultiStimulus());
00345 
00346     // Check which of the defined stimuli contain the current node
00347     for (unsigned stimulus_index = 0;
00348          stimulus_index < mStimuliApplied.size();
00349          ++stimulus_index)
00350     {
00351         if ( mStimulatedAreas[stimulus_index]->DoesContain(this->GetMesh()->GetNode(nodeIndex)->GetPoint()) )
00352         {
00353             node_specific_stimulus->AddStimulus(mStimuliApplied[stimulus_index]);
00354         }
00355     }
00356 
00357     return CreateCellWithIntracellularStimulus(node_specific_stimulus, nodeIndex);
00358 }
00359 
00360 template<unsigned SPACE_DIM>
00361 void HeartConfigRelatedCellFactory<SPACE_DIM>::FillInCellularTransmuralAreas()
00362 {
00363     NEVER_REACHED;
00364 }
00365 
00366 template<>
00367 void HeartConfigRelatedCellFactory<3u>::FillInCellularTransmuralAreas()
00368 {
00369     std::string mesh_file_name = HeartConfig::Instance()->GetMeshName();
00370     //files containing list of nodes on each surface
00371     std::string epi_surface = mesh_file_name + ".epi";
00372     std::string lv_surface = mesh_file_name + ".lv";
00373     std::string rv_surface = mesh_file_name + ".rv";
00374 
00375 
00376     //create the HeartGeometryInformation object
00377     //HeartGeometryInformation<3u> info(mesh, epi_surface, lv_surface, rv_surface, true);
00378     HeartGeometryInformation<3u> info(*(this->GetMesh()), epi_surface, lv_surface, rv_surface, true);
00379 
00380     //We need the fractions of epi and endo layer supplied by the user
00381     double epi_fraction = HeartConfig::Instance()->GetEpiLayerFraction();
00382     double endo_fraction = HeartConfig::Instance()->GetEndoLayerFraction();
00383 
00384     //given the fraction of each layer, compute the distance map and fill in the vector
00385     info.DetermineLayerForEachNode(epi_fraction,endo_fraction);
00386     //get the big heterogeneity vector
00387     std::vector<unsigned> heterogeneity_node_list;
00388     for (unsigned index=0; index<this->GetMesh()->GetNumNodes(); index++)
00389     {
00390         heterogeneity_node_list.push_back(info.rGetLayerForEachNode()[index]);
00391     }
00392 
00393     std::vector<Node<3u>*> epi_nodes;
00394     std::vector<Node<3u>*> mid_nodes;
00395     std::vector<Node<3u>*> endo_nodes;
00396 
00397     //create the list of (pointer to object) nodes in each layer from the heterogeneities vector that was just filled in
00398     for (unsigned node_index = 0; node_index < this->GetMesh()->GetNumNodes(); node_index++)
00399     {
00400         if (this->GetMesh()->GetDistributedVectorFactory()->IsGlobalIndexLocal(node_index) )
00401         {
00402             switch (heterogeneity_node_list[node_index])
00403             {
00404                 //epi
00405                 case 2u:
00406                 {
00407                     epi_nodes.push_back(this->GetMesh()->GetNode(node_index));
00408                     break;
00409                 }
00410                 //mid
00411                 case 1u:
00412                 {
00413                     mid_nodes.push_back(this->GetMesh()->GetNode(node_index));
00414                     break;
00415                 }
00416                 //endo
00417                 case 0u:
00418                 {
00419                     endo_nodes.push_back(this->GetMesh()->GetNode(node_index));
00420                     break;
00421                 }
00422                 default:
00423                 NEVER_REACHED;
00424             }
00425         }
00426     }
00427     //assert((endo_nodes.size()+epi_nodes.size()+mid_nodes.size())==this->GetMesh()->GetNumNodes());
00428 
00429     // now the 3 list of pointer to nodes need to be pushed into the mCellHeterogeneityAreas vector,
00430     // IN THE ORDER PRESCRIBED BY THE USER IN THE XML FILE!
00431     // This is because the corresponding scale factors are already read in that order.
00432 
00433     //these three unsigned tell us in which order the user supplied each layer in the XML file
00434     unsigned user_supplied_epi_index = HeartConfig::Instance()->GetEpiLayerIndex();
00435     unsigned user_supplied_mid_index = HeartConfig::Instance()->GetMidLayerIndex();
00436     unsigned user_supplied_endo_index = HeartConfig::Instance()->GetEndoLayerIndex();
00437 
00438     //these three should have been set to 0, 1 and 2 by HeartConfig::GetCellHeterogeneities
00439     assert(user_supplied_epi_index<3);
00440     assert(user_supplied_mid_index<3);
00441     assert(user_supplied_endo_index<3);
00442 
00443     //pute them in a vector
00444     std::vector<unsigned> user_supplied_indices;
00445     user_supplied_indices.push_back(user_supplied_epi_index);
00446     user_supplied_indices.push_back(user_supplied_mid_index);
00447     user_supplied_indices.push_back(user_supplied_endo_index);
00448 
00449     //figure out who goes first
00450 
00451     //loop three times
00452     for (unsigned layer_index=0; layer_index<3; layer_index++)
00453     {
00454         unsigned counter = 0;
00455         //find the corresponding index
00456         for (unsigned supplied_index = 0; supplied_index<user_supplied_indices.size(); supplied_index++)
00457         {
00458             if (user_supplied_indices[supplied_index] == layer_index)
00459             {
00460                 break;
00461             }
00462             counter++;
00463         }
00464 
00465         //create the node lists based on the calculations above
00466         if (counter==0)
00467         {
00468             mCellHeterogeneityAreas.push_back(boost::shared_ptr<AbstractChasteRegion<3u> >(new ChasteNodesList<3u>(epi_nodes)) );
00469         }
00470         if (counter==1)
00471         {
00472             mCellHeterogeneityAreas.push_back(boost::shared_ptr<AbstractChasteRegion<3u> >(new ChasteNodesList<3u>(mid_nodes)) );
00473         }
00474         if (counter==2)
00475         {
00476             mCellHeterogeneityAreas.push_back(boost::shared_ptr<AbstractChasteRegion<3u> >(new ChasteNodesList<3u>(endo_nodes)) );
00477         }
00478         assert(counter<3);
00479     }
00480     assert(mCellHeterogeneityAreas.size()==3);
00481 }
00482 
00483 // Explicit instantiation
00484 template class HeartConfigRelatedCellFactory<1u>;
00485 template class HeartConfigRelatedCellFactory<2u>;
00486 template class HeartConfigRelatedCellFactory<3u>;