Chaste Release::3.1
|
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)