Chaste Release::3.1
ExtendedBidomainProblem.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 
00037 #include "ExtendedBidomainProblem.hpp"
00038 #include "ExtendedBidomainSolver.hpp"
00039 #include "AbstractDynamicLinearPdeSolver.hpp"
00040 #include "HeartConfig.hpp"
00041 #include "Exception.hpp"
00042 #include "DistributedVector.hpp"
00043 #include "ReplicatableVector.hpp"
00044 
00045 
00046 
00047 template<unsigned DIM>
00048 ExtendedBidomainProblem<DIM>::ExtendedBidomainProblem(
00049             AbstractCardiacCellFactory<DIM>* pCellFactory, AbstractCardiacCellFactory<DIM>* pSecondCellFactory, bool hasBath)
00050     : AbstractCardiacProblem<DIM,DIM, 3>(pCellFactory),
00051       mpSecondCellFactory(pSecondCellFactory),
00052       mpExtendedBidomainTissue(NULL),
00053       mUserSpecifiedSecondCellConductivities(false),
00054       mUserHasSetBidomainValuesExplicitly(false),
00055       mpExtracellularStimulusFactory(NULL),
00056       mRowForAverageOfPhiZeroed(INT_MAX),
00057       mApplyAveragePhieZeroConstraintAfterSolving(false),
00058       mUserSuppliedExtracellularStimulus(false),
00059       mHasBath(hasBath)
00060 {
00061     mFixedExtracellularPotentialNodes.resize(0);
00062 }
00063 
00064 template<unsigned DIM>
00065 ExtendedBidomainProblem<DIM>::ExtendedBidomainProblem()
00066     : AbstractCardiacProblem<DIM,DIM, 3>(),
00067       mpSecondCellFactory(NULL),
00068       mpExtendedBidomainTissue(NULL),
00069       mUserSpecifiedSecondCellConductivities(false),
00070       mUserHasSetBidomainValuesExplicitly(false),
00071       mpExtracellularStimulusFactory(NULL),
00072       mRowForAverageOfPhiZeroed(INT_MAX),
00073       mApplyAveragePhieZeroConstraintAfterSolving(false),
00074       mUserSuppliedExtracellularStimulus(false)
00075 {
00076     mFixedExtracellularPotentialNodes.resize(0);
00077 }
00078 
00079 
00080 template<unsigned DIM>
00081 Vec ExtendedBidomainProblem<DIM>::CreateInitialCondition()
00082 {
00083 
00084     DistributedVectorFactory* p_factory = this->mpMesh->GetDistributedVectorFactory();
00085     Vec initial_condition = p_factory->CreateVec(3);
00086     DistributedVector ic = p_factory->CreateDistributedVector(initial_condition);
00087     std::vector<DistributedVector::Stripe> stripe;
00088     stripe.reserve(3);
00089 
00090     stripe.push_back(DistributedVector::Stripe(ic, 0));
00091     stripe.push_back(DistributedVector::Stripe(ic, 1));
00092     stripe.push_back(DistributedVector::Stripe(ic, 2));
00093 
00094     for (DistributedVector::Iterator index = ic.Begin();
00095          index != ic.End();
00096          ++index)
00097     {
00098         stripe[0][index] = mpExtendedBidomainTissue->GetCardiacCell(index.Global)->GetVoltage();//phi_i of frrst cell = Vm first cell at the start
00099         stripe[1][index] = mpExtendedBidomainTissue->GetCardiacSecondCell(index.Global)->GetVoltage();//phi_i of second cell = Vm second cell at the start
00100         stripe[2][index] = 0.0;//
00101     }
00102     ic.Restore();
00103 
00104     return initial_condition;
00105 }
00106 
00107 template<unsigned DIM>
00108 void ExtendedBidomainProblem<DIM>::ProcessExtracellularStimulus()
00109 {
00110     if ((mpExtracellularStimulusFactory == NULL))//user has not passed in any extracelular stimulus in any form
00111     {
00112         mpExtracellularStimulusFactory = new AbstractStimulusFactory<DIM>();
00113         //create one (with default implementation to zero stimulus everywhere)
00114     }
00115 
00116     assert(mpExtracellularStimulusFactory);//should be created by now, either above or by the user...
00117     mpExtracellularStimulusFactory->SetMesh(this->mpMesh);//so, set the mesh into it.
00118     mpExtracellularStimulusFactory->SetCompatibleExtracellularStimulus();//make sure compatibility condition will be valid
00119 
00120     std::vector<AbstractChasteRegion<DIM>* > grounded_regions = mpExtracellularStimulusFactory->GetRegionsToBeGrounded();
00121 
00122     if ( (mUserSuppliedExtracellularStimulus) && grounded_regions.size() > 0 ) //we check for grunded nodes here
00123     {
00124         std::vector<unsigned> grounded_indices;
00125         for (unsigned global_node_index = 0; global_node_index < this->mpMesh->GetNumNodes(); global_node_index++)
00126         {
00127             if (this->mpMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(global_node_index))
00128             {
00129                 for (unsigned region_index = 0; region_index <grounded_regions.size(); region_index++)
00130                 {
00131                     if ( grounded_regions[region_index]->DoesContain( this->mpMesh->GetNode(global_node_index)->GetPoint() ) )
00132                     {
00133                         grounded_indices.push_back( this->mpMesh->GetNode(global_node_index)->GetIndex() );
00134                     }
00135                 }
00136             }
00137         }
00138         PetscTools::Barrier();
00139         SetFixedExtracellularPotentialNodes(grounded_indices);
00140     }
00141 }
00142 
00143 template<unsigned DIM>
00144 AbstractCardiacTissue<DIM> * ExtendedBidomainProblem<DIM>::CreateCardiacTissue()
00145 {
00146     //set the mesh into the second cell factory as well.
00147     mpSecondCellFactory->SetMesh(this->mpMesh);
00148 
00149     //deal with extracellular stimulus, if any
00150     ProcessExtracellularStimulus();
00151 
00152     //Now create the tissue object
00153     mpExtendedBidomainTissue = new ExtendedBidomainTissue<DIM>(this->mpCellFactory, mpSecondCellFactory,mpExtracellularStimulusFactory);
00154 
00155     //Let the Tissue know if the user wants an extracellular stimulus (or if we had to create a default zero one).
00156     mpExtendedBidomainTissue->SetUserSuppliedExtracellularStimulus(mUserSuppliedExtracellularStimulus);
00157 
00158     //if the user remembered to set a different value for the sigma of the second cell...
00159     if (mUserSpecifiedSecondCellConductivities)
00160     {
00161         mpExtendedBidomainTissue->SetIntracellularConductivitiesSecondCell(mIntracellularConductivitiesSecondCell);
00162     }
00163     else //..otherwise it gets the same as the first cell (according to heartconfig...)
00164     {
00165         c_vector<double, DIM> intra_conductivities;
00166         HeartConfig::Instance()->GetIntracellularConductivities(intra_conductivities);
00167         mpExtendedBidomainTissue->SetIntracellularConductivitiesSecondCell(intra_conductivities);
00168     }
00169 
00170     //the conductivities for the first cell are created within the tissue constructor in the abstract class
00171     //here we create the ones for the second cell
00172     mpExtendedBidomainTissue->CreateIntracellularConductivityTensorSecondCell();
00173 
00174     if (mUserHasSetBidomainValuesExplicitly)
00175     {
00176         mpExtendedBidomainTissue->SetAmFirstCell(mAmFirstCell);
00177         mpExtendedBidomainTissue->SetAmSecondCell(mAmSecondCell);
00178         mpExtendedBidomainTissue->SetAmGap(mAmGap);
00179         mpExtendedBidomainTissue->SetGGap(mGGap);
00180         mpExtendedBidomainTissue->SetCmFirstCell(mCmFirstCell);
00181         mpExtendedBidomainTissue->SetCmSecondCell(mCmSecondCell);
00182     }
00183     else//we set all the Am and Cm to the values set by the heartconfig (only one value for all Am and one value for all Cms)
00184     {
00185         mpExtendedBidomainTissue->SetAmFirstCell(HeartConfig::Instance()->GetSurfaceAreaToVolumeRatio());
00186         mpExtendedBidomainTissue->SetAmSecondCell(HeartConfig::Instance()->GetSurfaceAreaToVolumeRatio());
00187         mpExtendedBidomainTissue->SetAmGap(HeartConfig::Instance()->GetSurfaceAreaToVolumeRatio());
00188         mpExtendedBidomainTissue->SetGGap(0.0);
00189         mpExtendedBidomainTissue->SetCmFirstCell(HeartConfig::Instance()->GetCapacitance());
00190         mpExtendedBidomainTissue->SetCmSecondCell(HeartConfig::Instance()->GetCapacitance());
00191     }
00192 
00193     mpExtendedBidomainTissue->SetGgapHeterogeneities(mGgapHeterogeneityRegions, mGgapHeterogenousValues);//set user input into the tissue class
00194     mpExtendedBidomainTissue->CreateGGapConductivities();//if vectors are empty, mGgap will be put everywhere by this method.
00195 
00196     return mpExtendedBidomainTissue;
00197 }
00198 
00199 template<unsigned DIM>
00200 void ExtendedBidomainProblem<DIM>::SetExtendedBidomainParameters(double Am1, double Am2, double AmGap, double Cm1, double Cm2, double Ggap)
00201 {
00202      mAmFirstCell = Am1;
00203      mAmSecondCell = Am2;
00204      mAmGap = AmGap;
00205      mCmFirstCell = Cm1;
00206      mCmSecondCell = Cm2;
00207      mGGap = Ggap;
00208 
00209      mUserHasSetBidomainValuesExplicitly = true;
00210 }
00211 
00212 template <unsigned DIM>
00213 void ExtendedBidomainProblem<DIM>::SetGgapHeterogeneities ( std::vector<boost::shared_ptr<AbstractChasteRegion<DIM> > >& rGgapHeterogeneityRegions, std::vector<double>& rGgapValues)
00214 {
00215     if (rGgapHeterogeneityRegions.size() != rGgapValues.size() )
00216     {
00217         EXCEPTION  ("Gap junction heterogeneity areas must be of the same number as the heterogeneity values");
00218     }
00219     mGgapHeterogeneityRegions = rGgapHeterogeneityRegions;
00220     mGgapHeterogenousValues =rGgapValues;
00221 }
00222 
00223 template <unsigned DIM>
00224 void ExtendedBidomainProblem<DIM>::SetExtracellularStimulusFactory( AbstractStimulusFactory<DIM>* pFactory)
00225 {
00226     mpExtracellularStimulusFactory = pFactory;
00227     mUserSuppliedExtracellularStimulus = true;
00228 }
00229 
00230 template<unsigned DIM>
00231 AbstractDynamicLinearPdeSolver<DIM, DIM, 3>* ExtendedBidomainProblem<DIM>::CreateSolver()
00232 {
00233     /*
00234      * NOTE: The this->mpBoundaryConditionsContainer.get() lines below convert a
00235      * boost::shared_ptr to a normal pointer, as this is what the assemblers are
00236      * expecting. We have to be a bit careful though as boost could decide to delete
00237      * them whenever it feels like as it won't count the assembers as using them.
00238      *
00239      * As long as they are kept as member variables here for as long as they are
00240      * required in the assemblers it should all work OK.
00241      */
00242 
00243 
00244     mpSolver = new ExtendedBidomainSolver<DIM,DIM>( mHasBath,
00245                                                     this->mpMesh,
00246                                                     mpExtendedBidomainTissue,
00247                                                     this->mpBoundaryConditionsContainer.get(),
00248                                                     2);//2 is number of quad points
00249 
00250 
00251     try
00252     {
00253         mpSolver->SetFixedExtracellularPotentialNodes(mFixedExtracellularPotentialNodes);
00254         mpSolver->SetRowForAverageOfPhiZeroed(mRowForAverageOfPhiZeroed);
00255     }
00256     catch (const Exception& e)
00257     {
00258         delete mpSolver;
00259         throw e;
00260     }
00261 
00262     return mpSolver;
00263 }
00264 
00265 template<unsigned DIM>
00266 ExtendedBidomainProblem<DIM>::~ExtendedBidomainProblem()
00267 {
00268     if (!mUserSuppliedExtracellularStimulus)
00269     {
00270         delete mpExtracellularStimulusFactory;
00271     }
00272 }
00273 
00274 template<unsigned DIM>
00275 void ExtendedBidomainProblem<DIM>::SetIntracellularConductivitiesForSecondCell(c_vector<double, DIM> conductivities)
00276 {
00277     for (unsigned i = 0; i < DIM; i++)
00278     {
00279         mIntracellularConductivitiesSecondCell[i] = conductivities[i];
00280     }
00281     mUserSpecifiedSecondCellConductivities = true;
00282 }
00283 
00284 template<unsigned DIM>
00285 void ExtendedBidomainProblem<DIM>::SetFixedExtracellularPotentialNodes(std::vector<unsigned> nodes)
00286 {
00287     assert(mFixedExtracellularPotentialNodes.size() == 0); //TODO turn this into an exception if the user calls this twice...
00288     mFixedExtracellularPotentialNodes.resize(nodes.size());
00289     for (unsigned i=0; i<nodes.size(); i++)
00290     {
00291         // the assembler checks that the nodes[i] is less than
00292         // the number of nodes in the mesh so this is not done here
00293         mFixedExtracellularPotentialNodes[i] = nodes[i];
00294     }
00295 }
00296 
00297 template<unsigned DIM>
00298 void ExtendedBidomainProblem<DIM>::SetNodeForAverageOfPhiZeroed(unsigned node)
00299 {
00300     if (node==0)
00301     {
00302         mRowForAverageOfPhiZeroed = 2;
00303     }
00304     else
00305     {
00306         //Phie is every three lines, starting from zero...
00307         mRowForAverageOfPhiZeroed = 3*node  - 1;
00308     }
00309 }
00310 
00311 template<unsigned DIM>
00312 ExtendedBidomainTissue<DIM>* ExtendedBidomainProblem<DIM>::GetExtendedBidomainTissue()
00313 {
00314     assert(mpExtendedBidomainTissue!=NULL);
00315     return mpExtendedBidomainTissue;
00316 }
00317 
00318 template<unsigned DIM>
00319 void ExtendedBidomainProblem<DIM>::WriteInfo(double time)
00320 {
00321     if (PetscTools::AmMaster())
00322     {
00323         std::cout << "Solved to time " << time << "\n" << std::flush;
00324     }
00325 
00326     double phi_i_max_first_cell, phi_i_min_first_cell, phi_i_max_second_cell, phi_i_min_second_cell, phi_e_min, phi_e_max;
00327 
00328     VecSetBlockSize( this->mSolution, 3 );
00329 
00330     VecStrideMax( this->mSolution, 0, PETSC_NULL, &phi_i_max_first_cell );
00331     VecStrideMin( this->mSolution, 0, PETSC_NULL, &phi_i_min_first_cell );
00332 
00333     VecStrideMax( this->mSolution, 1, PETSC_NULL, &phi_i_max_second_cell );
00334     VecStrideMin( this->mSolution, 1, PETSC_NULL, &phi_i_min_second_cell );
00335 
00336     VecStrideMax( this->mSolution, 2, PETSC_NULL, &phi_e_max );
00337     VecStrideMin( this->mSolution, 2, PETSC_NULL, &phi_e_min );
00338 
00339     if (PetscTools::AmMaster())
00340     {
00341         std::cout << " Phi_i first cell = " << "[" <<phi_i_max_first_cell << ", " << phi_i_min_first_cell << "]" << ";\n"
00342                   << " Phi_i second cell = " << "[" <<phi_i_max_second_cell << ", " << phi_i_min_second_cell << "]" << ";\n"
00343                   << " Phi_e = " << "[" <<phi_e_max << ", " << phi_e_min << "]" << ";\n"
00344                   << std::flush;
00345     }
00346 }
00347 
00348 template<unsigned DIM>
00349 void ExtendedBidomainProblem<DIM>::DefineWriterColumns(bool extending)
00350 {
00351     if (!extending)
00352     {
00353         if ( this->mNodesToOutput.empty() )
00354         {
00355             //Set writer to output all nodes
00356             this->mpWriter->DefineFixedDimension( this->mpMesh->GetNumNodes() );
00357         }
00358 //        else
00359 //        {
00360 //            //Output only the nodes indicted
00361 //            this->mpWriter->DefineFixedDimension( this->mNodesToOutput, this->mpMesh->GetNumNodes() );
00362 //        }
00363         //mNodeColumnId = mpWriter->DefineVariable("Node", "dimensionless");
00364         mVoltageColumnId_Vm1 = this->mpWriter->DefineVariable("V","mV");
00365         mVoltageColumnId_Vm2 = this->mpWriter->DefineVariable("V_2","mV");
00366         mVoltageColumnId_Phie = this->mpWriter->DefineVariable("Phi_e","mV");
00367         mVariablesIDs.push_back(mVoltageColumnId_Vm1);
00368         mVariablesIDs.push_back(mVoltageColumnId_Vm2);
00369         mVariablesIDs.push_back(mVoltageColumnId_Phie);
00370         this->mpWriter->DefineUnlimitedDimension("Time","msecs");
00371     }
00372     else
00373     {
00374         mVoltageColumnId_Vm1 = this->mpWriter->GetVariableByName("V");
00375         mVoltageColumnId_Vm2 = this->mpWriter->GetVariableByName("V_2");
00376         mVoltageColumnId_Phie = this->mpWriter->GetVariableByName("Phi_e");
00377     }
00378     //define any extra variable. NOTE: it must be in the first cell (not the second)
00379     AbstractCardiacProblem<DIM,DIM,3>::DefineExtraVariablesWriterColumns(extending);
00380 
00381 }
00382 
00383 template<unsigned DIM>
00384 void ExtendedBidomainProblem<DIM>::WriteOneStep(double time, Vec voltageVec)
00385 {
00386     this->mpWriter->PutUnlimitedVariable(time);
00387 
00388    // Create a striped vector
00389     Vec ordered_voltages =  this->mpMesh->GetDistributedVectorFactory()->CreateVec(3);
00390     DistributedVector wrapped_ordered_solution = this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(ordered_voltages);
00391     DistributedVector wrapped_solution = this->mpMesh->GetDistributedVectorFactory()->CreateDistributedVector(voltageVec);
00392 
00393     DistributedVector::Stripe phi_i_first_cell_stripe(wrapped_solution,0);
00394     DistributedVector::Stripe phi_i_second_cell_stripe(wrapped_solution,1);
00395     DistributedVector::Stripe phi_e_stripe(wrapped_solution,2);
00396 
00397     DistributedVector::Stripe wrapped_ordered_solution_first_stripe(wrapped_ordered_solution,0);
00398     DistributedVector::Stripe wrapped_ordered_solution_second_stripe(wrapped_ordered_solution,1);
00399     DistributedVector::Stripe wrapped_ordered_solution_third_stripe(wrapped_ordered_solution,2);
00400 
00401     for (DistributedVector::Iterator index = wrapped_solution.Begin();
00402          index != wrapped_solution.End();
00403          ++index)
00404     {
00405         wrapped_ordered_solution_first_stripe[index] = phi_i_first_cell_stripe[index] - phi_e_stripe[index];
00406         wrapped_ordered_solution_second_stripe[index] = phi_i_second_cell_stripe[index] - phi_e_stripe[index];
00407         wrapped_ordered_solution_third_stripe[index] = phi_e_stripe[index];
00408     }
00409     wrapped_solution.Restore();
00410     wrapped_ordered_solution.Restore();
00411 
00412     this->mpWriter->PutStripedVector(mVariablesIDs, ordered_voltages);
00413     PetscTools::Destroy(ordered_voltages);
00414     //write any extra variable. Note that this method in the parent class will
00415     //take the extra variable only from the first cell. ///\todo write a specific method for this class
00416     AbstractCardiacProblem<DIM,DIM,3>::WriteExtraVariablesOneStep();
00417 }
00418 
00419 template<unsigned DIM>
00420 void ExtendedBidomainProblem<DIM>::PreSolveChecks()
00421 {
00422     AbstractCardiacProblem<DIM,DIM, 3>::PreSolveChecks();
00423     if (mFixedExtracellularPotentialNodes.empty())
00424     {
00425         // We're not pinning any nodes.
00426         if (mRowForAverageOfPhiZeroed==INT_MAX)
00427         {
00428             // We're not using the constrain Average phi_e to 0 method, hence use a null space
00429             // Check that the KSP solver isn't going to do anything stupid:
00430             // phi_e is not bounded, so it'd be wrong to use a relative tolerance
00431             if (HeartConfig::Instance()->GetUseRelativeTolerance())
00432             {
00433                 EXCEPTION("Bidomain external voltage is not bounded in this simulation - use KSP *absolute* tolerance");
00434             }
00435         }
00436     }
00437 }
00438 
00439 template<unsigned DIM>
00440 bool ExtendedBidomainProblem<DIM>::GetHasBath()
00441 {
00442     return mHasBath;
00443 }
00444 
00445 
00446 template<unsigned DIM>
00447 void ExtendedBidomainProblem<DIM>::SetHasBath(bool hasBath)
00448 {
00449     mHasBath = hasBath;
00450 }
00451 
00453 // Explicit instantiation
00455 
00456 template class ExtendedBidomainProblem<1>;
00457 template class ExtendedBidomainProblem<2>;
00458 template class ExtendedBidomainProblem<3>;
00459 
00460 // Serialization for Boost >= 1.36
00461 #include "SerializationExportWrapperForCpp.hpp"
00462 EXPORT_TEMPLATE_CLASS_SAME_DIMS(ExtendedBidomainProblem)