ExtendedBidomainProblem.cpp

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 
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 extracellular 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 assemblers 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     mpSolver = new ExtendedBidomainSolver<DIM,DIM>( mHasBath,
00244                                                     this->mpMesh,
00245                                                     mpExtendedBidomainTissue,
00246                                                     this->mpBoundaryConditionsContainer.get());
00247 
00248     try
00249     {
00250         mpSolver->SetFixedExtracellularPotentialNodes(mFixedExtracellularPotentialNodes);
00251         mpSolver->SetRowForAverageOfPhiZeroed(mRowForAverageOfPhiZeroed);
00252     }
00253     catch (const Exception& e)
00254     {
00255         delete mpSolver;
00256         throw e;
00257     }
00258 
00259     return mpSolver;
00260 }
00261 
00262 template<unsigned DIM>
00263 ExtendedBidomainProblem<DIM>::~ExtendedBidomainProblem()
00264 {
00265     if (!mUserSuppliedExtracellularStimulus)
00266     {
00267         delete mpExtracellularStimulusFactory;
00268     }
00269 }
00270 
00271 template<unsigned DIM>
00272 void ExtendedBidomainProblem<DIM>::SetIntracellularConductivitiesForSecondCell(c_vector<double, DIM> conductivities)
00273 {
00274     for (unsigned i = 0; i < DIM; i++)
00275     {
00276         mIntracellularConductivitiesSecondCell[i] = conductivities[i];
00277     }
00278     mUserSpecifiedSecondCellConductivities = true;
00279 }
00280 
00281 template<unsigned DIM>
00282 void ExtendedBidomainProblem<DIM>::SetFixedExtracellularPotentialNodes(std::vector<unsigned> nodes)
00283 {
00284     assert(mFixedExtracellularPotentialNodes.size() == 0); 
00285     mFixedExtracellularPotentialNodes.resize(nodes.size());
00286     for (unsigned i=0; i<nodes.size(); i++)
00287     {
00288         // the assembler checks that the nodes[i] is less than
00289         // the number of nodes in the mesh so this is not done here
00290         mFixedExtracellularPotentialNodes[i] = nodes[i];
00291     }
00292 }
00293 
00294 template<unsigned DIM>
00295 void ExtendedBidomainProblem<DIM>::SetNodeForAverageOfPhiZeroed(unsigned node)
00296 {
00297     if (node==0)
00298     {
00299         mRowForAverageOfPhiZeroed = 2;
00300     }
00301     else
00302     {
00303         //Phie is every three lines, starting from zero...
00304         mRowForAverageOfPhiZeroed = 3*node  - 1;
00305     }
00306 }
00307 
00308 template<unsigned DIM>
00309 ExtendedBidomainTissue<DIM>* ExtendedBidomainProblem<DIM>::GetExtendedBidomainTissue()
00310 {
00311     assert(mpExtendedBidomainTissue!=NULL);
00312     return mpExtendedBidomainTissue;
00313 }
00314 
00315 template<unsigned DIM>
00316 void ExtendedBidomainProblem<DIM>::WriteInfo(double time)
00317 {
00318     if (PetscTools::AmMaster())
00319     {
00320         std::cout << "Solved to time " << time << "\n" << std::flush;
00321     }
00322 
00323     double V_max_first_cell, V_min_first_cell, V_max_second_cell, V_min_second_cell, phi_e_min, phi_e_max;
00324 
00325     VecStrideMax( this->mSolution, 0, PETSC_NULL, &V_max_first_cell );
00326     VecStrideMin( this->mSolution, 0, PETSC_NULL, &V_min_first_cell );
00327 
00328     VecStrideMax( this->mSolution, 1, PETSC_NULL, &V_max_second_cell );
00329     VecStrideMin( this->mSolution, 1, PETSC_NULL, &V_min_second_cell );
00330 
00331     VecStrideMax( this->mSolution, 2, PETSC_NULL, &phi_e_max );
00332     VecStrideMin( this->mSolution, 2, PETSC_NULL, &phi_e_min );
00333 
00334     if (PetscTools::AmMaster())
00335     {
00336         std::cout << " V first cell = "  << "[" <<V_min_first_cell << ",   " << V_max_first_cell << "]" << ";\n"
00337                   << " V second cell = " << "[" <<V_min_second_cell << ", " << V_max_second_cell << "]" << ";\n"
00338                   << " Phi_e = "         << "[" <<phi_e_min << ", "         << phi_e_max << "]" << ";\n"
00339                   << std::flush;
00340     }
00341 }
00342 
00343 template<unsigned DIM>
00344 void ExtendedBidomainProblem<DIM>::DefineWriterColumns(bool extending)
00345 {
00346     if (!extending)
00347     {
00348         if ( this->mNodesToOutput.empty() )
00349         {
00350             //Set writer to output all nodes
00351             this->mpWriter->DefineFixedDimension( this->mpMesh->GetNumNodes() );
00352         }
00353 //        else
00354 //        {
00355 //            //Output only the nodes indicted
00356 //            this->mpWriter->DefineFixedDimension( this->mNodesToOutput, this->mpMesh->GetNumNodes() );
00357 //        }
00358         //mNodeColumnId = mpWriter->DefineVariable("Node", "dimensionless");
00359         mVoltageColumnId_Vm1 = this->mpWriter->DefineVariable("V","mV");
00360         mVoltageColumnId_Vm2 = this->mpWriter->DefineVariable("V_2","mV");
00361         mVoltageColumnId_Phie = this->mpWriter->DefineVariable("Phi_e","mV");
00362         mVariablesIDs.push_back(mVoltageColumnId_Vm1);
00363         mVariablesIDs.push_back(mVoltageColumnId_Vm2);
00364         mVariablesIDs.push_back(mVoltageColumnId_Phie);
00365 
00366         // Only used to get an estimate of the # of timesteps below (copied from Abstract class)
00367         TimeStepper stepper(AbstractCardiacProblem<DIM,DIM,3>::mCurrentTime,
00368                             HeartConfig::Instance()->GetSimulationDuration(),
00369                             HeartConfig::Instance()->GetPrintingTimeStep());
00370         this->mpWriter->DefineUnlimitedDimension("Time", "msecs", stepper.EstimateTimeSteps()+1); // +1 for start and end
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 V_first_cell_stripe(wrapped_solution,0);
00394     DistributedVector::Stripe V_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] = V_first_cell_stripe[index];
00406         wrapped_ordered_solution_second_stripe[index] = V_second_cell_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.
00417     AbstractCardiacProblem<DIM,DIM,3>::WriteExtraVariablesOneStep();
00418 }
00419 
00420 template<unsigned DIM>
00421 void ExtendedBidomainProblem<DIM>::PreSolveChecks()
00422 {
00423     AbstractCardiacProblem<DIM,DIM, 3>::PreSolveChecks();
00424     if (mFixedExtracellularPotentialNodes.empty())
00425     {
00426         // We're not pinning any nodes.
00427         if (mRowForAverageOfPhiZeroed==INT_MAX)
00428         {
00429             // We're not using the constrain Average phi_e to 0 method, hence use a null space
00430             // Check that the KSP solver isn't going to do anything stupid:
00431             // phi_e is not bounded, so it'd be wrong to use a relative tolerance
00432             if (HeartConfig::Instance()->GetUseRelativeTolerance())
00433             {
00434                 EXCEPTION("Bidomain external voltage is not bounded in this simulation - use KSP *absolute* tolerance");
00435             }
00436         }
00437     }
00438 }
00439 
00440 template<unsigned DIM>
00441 bool ExtendedBidomainProblem<DIM>::GetHasBath()
00442 {
00443     return mHasBath;
00444 }
00445 
00446 
00447 template<unsigned DIM>
00448 void ExtendedBidomainProblem<DIM>::SetHasBath(bool hasBath)
00449 {
00450     mHasBath = hasBath;
00451 }
00452 
00454 // Explicit instantiation
00456 
00457 template class ExtendedBidomainProblem<1>;
00458 template class ExtendedBidomainProblem<2>;
00459 template class ExtendedBidomainProblem<3>;
00460 
00461 // Serialization for Boost >= 1.36
00462 #include "SerializationExportWrapperForCpp.hpp"
00463 EXPORT_TEMPLATE_CLASS_SAME_DIMS(ExtendedBidomainProblem)

Generated by  doxygen 1.6.2