Chaste Release::3.1
Electrodes.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 "Electrodes.hpp"
00037 #include "DistributedTetrahedralMesh.hpp"
00038 #include "IsNan.hpp"
00039 #include "HeartConfig.hpp"
00040 
00041 template<unsigned DIM>
00042 Electrodes<DIM>::Electrodes(AbstractTetrahedralMesh<DIM,DIM>& rMesh)
00043     : mpMesh(&rMesh),
00044       mLeftElectrodeArea(0.0),
00045       mRightElectrodeArea(0.0)
00046 {
00047     unsigned axis_index;
00048     double magnitude, duration;
00049 
00050     HeartConfig::Instance()->GetElectrodeParameters(mGroundSecondElectrode, axis_index, magnitude, mStartTime, duration);
00051 
00052     assert(axis_index < DIM);
00053     assert(duration > 0);
00054     mEndTime = mStartTime + duration;
00055     mAreActive = false; // consider electrodes initially switched off!
00056 
00057     ChasteCuboid<DIM> bounding_box = mpMesh->CalculateBoundingBox();
00058     double global_min = bounding_box.rGetLowerCorner()[axis_index];
00059     double global_max = bounding_box.rGetUpperCorner()[axis_index];
00060 
00061     mpBoundaryConditionsContainer.reset(new BoundaryConditionsContainer<DIM,DIM,2>);
00062 
00063 
00064     double input_flux;
00065     double output_flux;
00066 
00067     try
00068     {
00069         ComputeElectrodesAreasAndCheckEquality(axis_index, global_min, global_max);
00070         input_flux = magnitude;
00071         output_flux = -input_flux;
00072 
00073     }
00074     catch (Exception& e)
00075     {
00076         // magnitude of second electrode scaled so that left_area*magnitude_left = -right_area*magnitude_right
00077         input_flux = magnitude;
00078         output_flux = -input_flux*mLeftElectrodeArea/mRightElectrodeArea;
00079 
00080         // Paranoia. In case one of the areas is 0
00081         assert( ! std::isnan(output_flux));
00082         assert( output_flux != 0.0);
00083     }
00084 
00085     ConstBoundaryCondition<DIM>* p_bc_flux_in = new ConstBoundaryCondition<DIM>(input_flux);
00086     ConstBoundaryCondition<DIM>* p_bc_flux_out = new ConstBoundaryCondition<DIM>(output_flux);
00087 
00088     // loop over boundary elements and add a non-zero phi_e boundary condition (ie extracellular
00089     // stimulus) if (assuming axis_index=0, etc) x=lowerValue (where x is the x-value of the centroid)
00090     for (typename AbstractTetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00091             = mpMesh->GetBoundaryElementIteratorBegin();
00092        iter != mpMesh->GetBoundaryElementIteratorEnd();
00093        iter++)
00094     {
00095         if (fabs((*iter)->CalculateCentroid()[axis_index] - global_min) < 1e-6)
00096         {
00097             mpBoundaryConditionsContainer->AddNeumannBoundaryCondition(*iter, p_bc_flux_in,  1);
00098         }
00099 
00100         if (!mGroundSecondElectrode)
00101         {
00102             if (fabs((*iter)->CalculateCentroid()[axis_index] - global_max) < 1e-6)
00103             {
00104                 mpBoundaryConditionsContainer->AddNeumannBoundaryCondition(*iter, p_bc_flux_out, 1);
00105             }
00106         }
00107     }
00108 
00109     // set up mGroundedNodes using opposite surface ie second electrode is
00110     // grounded
00111     if (mGroundSecondElectrode)
00112     {
00113         ConstBoundaryCondition<DIM>* p_zero_bc = new ConstBoundaryCondition<DIM>(0.0);
00114         // We will need to recalculate this when HasDirichletBoundaryConditions() is called.
00115         this->mpBoundaryConditionsContainer->ResetDirichletCommunication();
00116 
00117         for (typename AbstractTetrahedralMesh<DIM,DIM>::NodeIterator iter=mpMesh->GetNodeIteratorBegin();
00118              iter != mpMesh->GetNodeIteratorEnd();
00119              ++iter)
00120         {
00121             if (fabs((*iter).rGetLocation()[axis_index]-global_max) < 1e-6)
00122             {
00123                 mpBoundaryConditionsContainer->AddDirichletBoundaryCondition(&(*iter), p_zero_bc, 1);
00124             }
00125         }
00126 
00127         //Unused boundary conditions will not be deleted by the b.c. container
00128         delete p_bc_flux_out;
00129     }
00130 }
00131 
00132 
00133 
00134 template<unsigned DIM>
00135 boost::shared_ptr<BoundaryConditionsContainer<DIM,DIM,2> > Electrodes<DIM>::GetBoundaryConditionsContainer()
00136 {
00137     //assert(mAreActive);
00138     return mpBoundaryConditionsContainer;
00139 }
00140 
00141 
00142 template<unsigned DIM>
00143 bool Electrodes<DIM>::SwitchOff(double time)
00144 {
00145     // This smidge has to be the same as the one below
00147     double smidge = 1e-10;
00148     if (mAreActive && time>mEndTime-smidge)
00149     {
00150         mAreActive = false;
00151         return true;
00152     }
00153 
00154     return false;
00155 }
00156 
00157 template<unsigned DIM>
00158 bool Electrodes<DIM>::SwitchOn(double time)
00159 {
00160     // This smidge has to be the same as the one above.
00162     double smidge = 1e-10;
00163     if (!mAreActive && time>=mStartTime && time<=mEndTime - smidge)
00164     {
00165         mAreActive = true;
00166         return true;
00167     }
00168 
00169     return false;
00170 }
00171 
00172 template<unsigned DIM>
00173 void Electrodes<DIM>::ComputeElectrodesAreasAndCheckEquality(unsigned dimensionIndex, double lowerValue, double upperValue)
00174 {
00175     //
00176     // loop over boundary elements and determine areas of the electrode faces
00177     //
00178     double local_left_area = 0.0;
00179     double local_right_area = 0.0;
00180 
00181     c_vector<double,DIM> weighted_direction;
00182     double jacobian_determinant;
00183 
00184 
00185     for (typename AbstractTetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00186              = mpMesh->GetBoundaryElementIteratorBegin();
00187          iter != mpMesh->GetBoundaryElementIteratorEnd();
00188          iter++)
00189     {
00190         if ( mpMesh->CalculateDesignatedOwnershipOfBoundaryElement( (*iter)->GetIndex() ))
00191         {
00192             if (fabs((*iter)->CalculateCentroid()[dimensionIndex] - lowerValue) < 1e-6)
00193             {
00194                 mpMesh->GetWeightedDirectionForBoundaryElement((*iter)->GetIndex(), weighted_direction, jacobian_determinant);
00195                 local_left_area += jacobian_determinant;
00196             }
00197 
00198             if (fabs((*iter)->CalculateCentroid()[dimensionIndex] - upperValue) < 1e-6)
00199             {
00200                 mpMesh->GetWeightedDirectionForBoundaryElement((*iter)->GetIndex(), weighted_direction, jacobian_determinant);
00201                 local_right_area += jacobian_determinant;
00202             }
00203         }
00204     }
00205 
00206     if(DIM==3)
00207     {
00208         // if the dimension of the face is 1, the mapping is from the face to the canonical element [0,1], so the
00209         // jacobian_determinants used above will be exactly the areas of the faces
00210         // if the dimension of the face is 1, the mapping is from the face to the canonical element, the triangle
00211         // with nodes (0,0), (0,1), (1,0), which has area 0.5, so scale the jacobian_determinants by 0.5 to
00212         // get the face areas, ie scale the total area by 0.5:
00213         // (Not technically needed as it is only the ratio of these that is used but since we are calling the variables
00214         // 'area's we ought to be correct).
00215         local_left_area /= 2.0;
00216         local_right_area /= 2.0;
00217     }
00218 
00219     int mpi_ret = MPI_Allreduce(&local_left_area, &mLeftElectrodeArea, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
00220     assert(mpi_ret == MPI_SUCCESS);
00221 
00222     mpi_ret = MPI_Allreduce(&local_right_area, &mRightElectrodeArea, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
00223     assert(mpi_ret == MPI_SUCCESS);
00224 
00225     if (mLeftElectrodeArea != mRightElectrodeArea)
00226     {
00227         EXCEPTION("Electrodes have different area");
00228     }
00229 }
00230 
00231 
00233 // Explicit instantiation
00235 
00236 template class Electrodes<1>;
00237 template class Electrodes<2>;
00238 template class Electrodes<3>;
00239 
00240 
00241 // Serialization for Boost >= 1.36
00242 #include "SerializationExportWrapperForCpp.hpp"
00243 EXPORT_TEMPLATE_CLASS_SAME_DIMS(Electrodes)