Electrodes.cpp

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

Generated by  doxygen 1.6.2