Electrodes.cpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2011
00004 
00005 University of Oxford means the Chancellor, Masters and Scholars of the
00006 University of Oxford, having an administrative office at Wellington
00007 Square, Oxford OX1 2JD, UK.
00008 
00009 This file is part of Chaste.
00010 
00011 Chaste is free software: you can redistribute it and/or modify it
00012 under the terms of the GNU Lesser General Public License as published
00013 by the Free Software Foundation, either version 2.1 of the License, or
00014 (at your option) any later version.
00015 
00016 Chaste is distributed in the hope that it will be useful, but WITHOUT
00017 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00018 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
00019 License for more details. The offer of Chaste under the terms of the
00020 License is subject to the License being interpreted in accordance with
00021 English Law and subject to any action against the University of Oxford
00022 being under the jurisdiction of the English Courts.
00023 
00024 You should have received a copy of the GNU Lesser General Public License
00025 along with Chaste. If not, see <http://www.gnu.org/licenses/>.
00026 
00027 */
00028 
00029 #include "Electrodes.hpp"
00030 #include "DistributedTetrahedralMesh.hpp"
00031 #include "IsNan.hpp"
00032 #include "HeartConfig.hpp"
00033 
00034 template<unsigned DIM>
00035 Electrodes<DIM>::Electrodes(AbstractTetrahedralMesh<DIM,DIM>& rMesh)
00036     : mpMesh(&rMesh),
00037       mLeftElectrodeArea(0.0),
00038       mRightElectrodeArea(0.0)
00039 {
00040     unsigned axis_index;
00041     double magnitude, duration;
00042 
00043     HeartConfig::Instance()->GetElectrodeParameters(mGroundSecondElectrode, axis_index, magnitude, mStartTime, duration);
00044 
00045     assert(axis_index < DIM);
00046     assert(duration > 0);
00047     mEndTime = mStartTime + duration;
00048     mAreActive = false; // consider electrodes initially switched off!
00049 
00050     ChasteCuboid<DIM> bounding_box = mpMesh->CalculateBoundingBox();
00051     double global_min = bounding_box.rGetLowerCorner()[axis_index];
00052     double global_max = bounding_box.rGetUpperCorner()[axis_index];
00053 
00054     mpBoundaryConditionsContainer.reset(new BoundaryConditionsContainer<DIM,DIM,2>);
00055 
00056 
00057     double input_flux;
00058     double output_flux;
00059 
00060     try
00061     {
00062         ComputeElectrodesAreasAndCheckEquality(axis_index, global_min, global_max);
00063         input_flux = magnitude;
00064         output_flux = -input_flux;
00065 
00066     }
00067     catch (Exception& e)
00068     {
00069         // magnitude of second electrode scaled so that left_area*magnitude_left = -right_area*magnitude_right
00070         input_flux = magnitude;
00071         output_flux = -input_flux*mLeftElectrodeArea/mRightElectrodeArea;
00072 
00073         // Paranoia. In case one of the areas is 0
00074         assert( ! std::isnan(output_flux));
00075         assert( output_flux != 0.0);
00076     }
00077 
00078     ConstBoundaryCondition<DIM>* p_bc_flux_in = new ConstBoundaryCondition<DIM>(input_flux);
00079     ConstBoundaryCondition<DIM>* p_bc_flux_out = new ConstBoundaryCondition<DIM>(output_flux);
00080 
00081     // loop over boundary elements and add a non-zero phi_e boundary condition (ie extracellular
00082     // stimulus) if (assuming axis_index=0, etc) x=lowerValue (where x is the x-value of the centroid)
00083     for (typename AbstractTetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00084             = mpMesh->GetBoundaryElementIteratorBegin();
00085        iter != mpMesh->GetBoundaryElementIteratorEnd();
00086        iter++)
00087     {
00088         if (fabs((*iter)->CalculateCentroid()[axis_index] - global_min) < 1e-6)
00089         {
00090             mpBoundaryConditionsContainer->AddNeumannBoundaryCondition(*iter, p_bc_flux_in,  1);
00091         }
00092 
00093         if (!mGroundSecondElectrode)
00094         {
00095             if (fabs((*iter)->CalculateCentroid()[axis_index] - global_max) < 1e-6)
00096             {
00097                 mpBoundaryConditionsContainer->AddNeumannBoundaryCondition(*iter, p_bc_flux_out, 1);
00098             }
00099         }
00100     }
00101 
00102     // set up mGroundedNodes using opposite surface ie second electrode is
00103     // grounded
00104     if (mGroundSecondElectrode)
00105     {
00106         ConstBoundaryCondition<DIM>* p_zero_bc = new ConstBoundaryCondition<DIM>(0.0);
00107         // We will need to recalculate this when HasDirichletBoundaryConditions() is called.
00108         this->mpBoundaryConditionsContainer->ResetDirichletCommunication();
00109 
00110         for (typename AbstractTetrahedralMesh<DIM,DIM>::NodeIterator iter=mpMesh->GetNodeIteratorBegin();
00111              iter != mpMesh->GetNodeIteratorEnd();
00112              ++iter)
00113         {
00114             if (fabs((*iter).rGetLocation()[axis_index]-global_max) < 1e-6)
00115             {
00116                 mpBoundaryConditionsContainer->AddDirichletBoundaryCondition(&(*iter), p_zero_bc, 1);
00117             }
00118         }
00119 
00120         //Unused boundary conditions will not be deleted by the b.c. container
00121         delete p_bc_flux_out;
00122     }
00123 }
00124 
00125 
00126 
00127 template<unsigned DIM>
00128 boost::shared_ptr<BoundaryConditionsContainer<DIM,DIM,2> > Electrodes<DIM>::GetBoundaryConditionsContainer()
00129 {
00130     //assert(mAreActive);
00131     return mpBoundaryConditionsContainer;
00132 }
00133 
00134 
00135 template<unsigned DIM>
00136 bool Electrodes<DIM>::SwitchOff(double time)
00137 {
00138     // This smidge has to be the same as the one below
00140     double smidge = 1e-10;
00141     if (mAreActive && time>mEndTime-smidge)
00142     {
00143         mAreActive = false;
00144         return true;
00145     }
00146 
00147     return false;
00148 }
00149 
00150 template<unsigned DIM>
00151 bool Electrodes<DIM>::SwitchOn(double time)
00152 {
00153     // This smidge has to be the same as the one above.
00155     double smidge = 1e-10;
00156     if (!mAreActive && time>=mStartTime && time<=mEndTime - smidge)
00157     {
00158         mAreActive = true;
00159         return true;
00160     }
00161 
00162     return false;
00163 }
00164 
00165 template<unsigned DIM>
00166 void Electrodes<DIM>::ComputeElectrodesAreasAndCheckEquality(unsigned dimensionIndex, double lowerValue, double upperValue)
00167 {
00168     //
00169     // loop over boundary elements and determine areas of the electrode faces
00170     //
00171     double local_left_area = 0.0;
00172     double local_right_area = 0.0;
00173 
00174     c_vector<double,DIM> weighted_direction;
00175     double jacobian_determinant;
00176 
00177 
00178     for (typename AbstractTetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter
00179              = mpMesh->GetBoundaryElementIteratorBegin();
00180          iter != mpMesh->GetBoundaryElementIteratorEnd();
00181          iter++)
00182     {
00183         if ( mpMesh->CalculateDesignatedOwnershipOfBoundaryElement( (*iter)->GetIndex() ))
00184         {
00185             if (fabs((*iter)->CalculateCentroid()[dimensionIndex] - lowerValue) < 1e-6)
00186             {
00187                 mpMesh->GetWeightedDirectionForBoundaryElement((*iter)->GetIndex(), weighted_direction, jacobian_determinant);
00188                 local_left_area += jacobian_determinant;
00189             }
00190 
00191             if (fabs((*iter)->CalculateCentroid()[dimensionIndex] - upperValue) < 1e-6)
00192             {
00193                 mpMesh->GetWeightedDirectionForBoundaryElement((*iter)->GetIndex(), weighted_direction, jacobian_determinant);
00194                 local_right_area += jacobian_determinant;
00195             }
00196         }
00197     }
00198 
00199     if(DIM==3)
00200     {
00201         // if the dimension of the face is 1, the mapping is from the face to the canonical element [0,1], so the
00202         // jacobian_determinants used above will be exactly the areas of the faces
00203         // if the dimension of the face is 1, the mapping is from the face to the canonical element, the triangle
00204         // with nodes (0,0), (0,1), (1,0), which has area 0.5, so scale the jacobian_determinants by 0.5 to
00205         // get the face areas, ie scale the total area by 0.5:
00206         // (Not technically needed as it is only the ratio of these that is used but since we are calling the variables
00207         // 'area's we ought to be correct).
00208         local_left_area /= 2.0;
00209         local_right_area /= 2.0;
00210     }
00211 
00212     int mpi_ret = MPI_Allreduce(&local_left_area, &mLeftElectrodeArea, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
00213     assert(mpi_ret == MPI_SUCCESS);
00214 
00215     mpi_ret = MPI_Allreduce(&local_right_area, &mRightElectrodeArea, 1, MPI_DOUBLE, MPI_SUM, PETSC_COMM_WORLD);
00216     assert(mpi_ret == MPI_SUCCESS);
00217 
00218     if (mLeftElectrodeArea != mRightElectrodeArea)
00219     {
00220         EXCEPTION("Electrodes have different area");
00221     }
00222 }
00223 
00224 
00226 // Explicit instantiation
00228 
00229 template class Electrodes<1>;
00230 template class Electrodes<2>;
00231 template class Electrodes<3>;
00232 
00233 
00234 // Serialization for Boost >= 1.36
00235 #include "SerializationExportWrapperForCpp.hpp"
00236 EXPORT_TEMPLATE_CLASS_SAME_DIMS(Electrodes)
Generated on Thu Dec 22 13:00:06 2011 for Chaste by  doxygen 1.6.3