Chaste Release::3.1
ElectrodesStimulusFactory.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 "ElectrodesStimulusFactory.hpp"
00037 #include "DistributedTetrahedralMesh.hpp"
00038 #include "IsNan.hpp"
00039 #include "HeartConfig.hpp"
00040 #include "GaussianQuadratureRule.hpp"
00041 
00042 template<unsigned DIM>
00043 ElectrodesStimulusFactory<DIM>::ElectrodesStimulusFactory(std::vector<std::pair<AbstractChasteRegion<DIM>*, AbstractChasteRegion<DIM>*> >& rElectrodePairs,
00044                                                           std::vector<double>& rStimulusMagnitudes,
00045                                                           std::vector<double>& rDurations,
00046                                                           std::vector<double>& rPeriods,
00047                                                           std::vector<double>& rStarts,
00048                                                           std::vector<double>& rEnds)
00049     : mrElectrodePairs(rElectrodePairs),
00050       mrMagnitudes(rStimulusMagnitudes),
00051       mrDurations(rDurations),
00052       mrPeriods(rPeriods),
00053       mrStarts(rStarts),
00054       mrEnds(rEnds),
00055       mGroundSecondElectrode(false)
00056 {
00057 
00058     if ( ( rElectrodePairs.size() != rStimulusMagnitudes.size() ) ||
00059        ( rElectrodePairs.size() != rDurations.size() ) ||
00060        ( rElectrodePairs.size() != rPeriods.size() ) ||
00061        ( rElectrodePairs.size() != rStarts.size() ) ||
00062        ( rElectrodePairs.size() != rEnds.size() ) )
00063     {
00064         EXCEPTION ("Vector of electrode pairs and vector of stimulation paremeters must have the same size");
00065     }
00066 
00067     mMagnitudesElectrode1 = mrMagnitudes;
00068     mMagnitudesElectrode2 = mrMagnitudes;
00069 }
00070 
00071 template<unsigned DIM>
00072 ElectrodesStimulusFactory<DIM>::~ElectrodesStimulusFactory()
00073 {
00074 }
00075 
00076 template<unsigned DIM>
00077 void ElectrodesStimulusFactory<DIM>::CheckForElectrodesIntersection()
00078 {
00079     std::vector<unsigned> nodes_in_all_electrodes;
00080     for (unsigned global_node_index = 0; global_node_index < this->mpMesh->GetNumNodes(); global_node_index++)
00081     {
00082         if (this->mpMesh->GetDistributedVectorFactory()->IsGlobalIndexLocal(global_node_index))
00083         {
00084             for (unsigned pair_index = 0; pair_index <mrElectrodePairs.size(); pair_index++)
00085             {
00086                 if ( mrElectrodePairs[pair_index].first->DoesContain( this->mpMesh->GetNode(global_node_index)->GetPoint() ) )
00087                 {
00088                     nodes_in_all_electrodes.push_back( this->mpMesh->GetNode(global_node_index)->GetIndex() );
00089                 }
00090                 if ( mrElectrodePairs[pair_index].second->DoesContain( this->mpMesh->GetNode(global_node_index)->GetPoint() ) )
00091                 {
00092                     nodes_in_all_electrodes.push_back( this->mpMesh->GetNode(global_node_index)->GetIndex() );
00093                 }
00094             }
00095         }
00096     }
00097     PetscTools::Barrier();
00098     for (unsigned node_index = 0; node_index < nodes_in_all_electrodes.size(); node_index++)
00099     {
00100         unsigned number_of_hits = 0;
00101         for (unsigned node_to_check = 0; node_to_check < nodes_in_all_electrodes.size(); node_to_check++)
00102         {
00103             if (nodes_in_all_electrodes[node_index] == nodes_in_all_electrodes[node_to_check] )
00104             {
00105                 number_of_hits++;
00106             }
00107         }
00108         if (number_of_hits>1)
00109         {
00110             EXCEPTION("Two or more electrodes intersect with each other");
00111         }
00112     }
00113 }
00114 
00115 template<unsigned DIM>
00116 void ElectrodesStimulusFactory<DIM>::GroundSecondElectrode(bool grounded)
00117 {
00118     mGroundSecondElectrode = grounded;
00119 }
00120 
00121 template<unsigned DIM>
00122 void ElectrodesStimulusFactory<DIM>::SetCompatibleExtracellularStimulus()
00123 {
00124     assert(this->mpMesh!=NULL);
00125     try
00126     {
00127         CheckForElectrodesIntersection();
00128     }
00129     catch (Exception &e)
00130     {
00131         PetscTools::ReplicateException(true); //Electrodes intersect
00132         throw e;
00133     }
00134     PetscTools::ReplicateException(false);
00135 
00136     for (unsigned pair_index = 0; pair_index < mrElectrodePairs.size(); pair_index++)
00137     {
00138 
00139         if (!mGroundSecondElectrode)//no grounding of second electrode
00140         {
00141             //compute the two fluxes for this pair
00142             double flux_electrode_1  = ComputeElectrodeTotalFlux(mrElectrodePairs[pair_index].first, mMagnitudesElectrode1[pair_index]);
00143             double flux_electrode_2  = ComputeElectrodeTotalFlux(mrElectrodePairs[pair_index].second, mMagnitudesElectrode2[pair_index]);
00144 
00145             //Scale the magnitude of the second electrode for this pair
00146             mMagnitudesElectrode2[pair_index] = -mMagnitudesElectrode1[pair_index]*flux_electrode_1/flux_electrode_2;
00147 
00148             // Paranoia.
00149             assert( flux_electrode_2 != 0.0);
00150             assert( ! std::isnan(mMagnitudesElectrode2[pair_index]));
00151 
00152         }
00153         else//second electrode is grounded
00154         {
00155             this->mGroundedRegions.push_back( mrElectrodePairs[pair_index].second );
00156         }
00157     }
00158 }
00159 
00160 template<unsigned DIM>
00161 boost::shared_ptr<AbstractStimulusFunction> ElectrodesStimulusFactory<DIM>::CreateStimulusForNode(unsigned nodeIndex)
00162 {
00163     boost::shared_ptr<RegularStimulus> p_stimulus;
00164     for (unsigned pair_index = 0; pair_index < mrElectrodePairs.size(); pair_index++)
00165     {
00166         if (mrElectrodePairs[pair_index].first->DoesContain(this->mpMesh->GetNode(nodeIndex)->GetPoint()) )
00167         {
00168             p_stimulus.reset( new RegularStimulus(mMagnitudesElectrode1[pair_index], mrDurations[pair_index], mrPeriods[pair_index], mrStarts[pair_index], mrEnds[pair_index]));
00169 
00170         }
00171         else if (mrElectrodePairs[pair_index].second->DoesContain(this->mpMesh->GetNode(nodeIndex)->GetPoint()) )
00172         {
00173             p_stimulus.reset ( new RegularStimulus(mMagnitudesElectrode2[pair_index], mrDurations[pair_index], mrPeriods[pair_index], mrStarts[pair_index], mrEnds[pair_index]));
00174 
00175         }
00176         else//no stimulus here
00177         {
00178             p_stimulus.reset ( new RegularStimulus(0.0, mrDurations[pair_index], mrPeriods[pair_index], mrStarts[pair_index], mrEnds[pair_index]));
00179         }
00180     }
00181     return p_stimulus;
00182 }
00183 
00184 template<unsigned DIM>
00185 double ElectrodesStimulusFactory<DIM>::ComputeElectrodeTotalFlux(AbstractChasteRegion<DIM>* pRegion, double stimulusMagnitude)
00186 {
00187         //assume 2 gauss points
00188         GaussianQuadratureRule<DIM>* pQuadRule = new GaussianQuadratureRule<DIM>(2u);
00189 
00190         //the basis functions
00191         c_vector<double, DIM+1> phi;
00192 
00193         double total_electrode_flux = 0.0;
00194         double ret;
00195 
00196         for (typename AbstractTetrahedralMesh<DIM,DIM>::NodeIterator iter=this->mpMesh->GetNodeIteratorBegin();
00197              iter != this->mpMesh->GetNodeIteratorEnd();
00198              ++iter)
00199         {
00200             if ( pRegion->DoesContain( (*iter).GetPoint() ) )
00201             {
00202                 unsigned node_index =     iter->GetIndex();
00203                 assert(node_index < this->mpMesh->GetNumNodes());
00204                 double contribution_of_this_node = 0.0;
00205                 //loop over the elements where this node is contained
00206                 for(std::set<unsigned>::iterator iter = this->mpMesh->GetNode(node_index)->rGetContainingElementIndices().begin();
00207                     iter != this->mpMesh->GetNode(node_index)->rGetContainingElementIndices().end();
00208                     ++iter)
00209                 {
00210                     Element<DIM,DIM>* p_element = this->mpMesh->GetElement(*iter);
00211 
00212                     /*Determine jacobian for this element*/
00213                     c_matrix<double, DIM, DIM> jacobian;
00214                     c_matrix<double, DIM, DIM> inverse_jacobian;//unused here, but needed for the function below
00215                     double jacobian_determinant;
00216                     this->mpMesh->GetInverseJacobianForElement(p_element->GetIndex(), jacobian, jacobian_determinant, inverse_jacobian);
00217 
00218                     double contribution_of_this_element = 0.0;//...to this node
00219                      // loop over Gauss points
00220                     for (unsigned quad_index=0; quad_index < pQuadRule->GetNumQuadPoints(); quad_index++)
00221                     {
00222                         const ChastePoint<DIM>& quad_point = pQuadRule->rGetQuadPoint(quad_index);
00223                         BasisFunction::ComputeBasisFunctions(quad_point, phi);
00224 
00225                         double interpolated_stimulus = 0.0;
00226                         //loop over nodes in this element
00227                         for (unsigned node_index_in_element = 0; node_index_in_element < p_element->GetNumNodes(); node_index_in_element++)
00228                         {
00229                             //const Node<DIM>* p_node = p_element->GetNode(node_index_in_element);
00230                             assert(p_element->GetNumNodes() == DIM+1);
00231                             interpolated_stimulus += stimulusMagnitude*phi(node_index_in_element);
00232                             contribution_of_this_element += interpolated_stimulus * phi(node_index_in_element) * jacobian_determinant * pQuadRule->GetWeight(quad_index);
00233                         }
00234 
00235                     }/*end of loop over gauss points*/
00236                     contribution_of_this_node += contribution_of_this_element;
00237 
00238                 }/*end of loop over elements where the node is contained*/
00239                 total_electrode_flux += contribution_of_this_node;
00240             }/* end of if that checks if node is in the electrode*/
00241         }/* end of loop over nodes in the mesh*/
00242 #ifndef NDEBUG
00243         int mpi_ret = MPI_Allreduce(&total_electrode_flux, &ret, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
00244         assert(mpi_ret == MPI_SUCCESS);
00245 #else
00246         MPI_Allreduce(&total_electrode_flux, &ret, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
00247 #endif
00248 
00249         //clear up memory
00250         delete pQuadRule;
00251 
00252         assert(ret < DBL_MAX);
00253         return ret;
00254 }
00255 
00257 // Explicit instantiation
00259 
00260 template class ElectrodesStimulusFactory<1>;
00261 template class ElectrodesStimulusFactory<2>;
00262 template class ElectrodesStimulusFactory<3>;
00263