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 #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)