Chaste Release::3.1
Element.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 "Element.hpp"
00037 
00038 #include <cfloat>
00039 #include <cassert>
00040 
00042 // Implementation
00044 
00045 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00046 Element<ELEMENT_DIM, SPACE_DIM>::Element(unsigned index, const std::vector<Node<SPACE_DIM>*>& rNodes, bool registerWithNodes)
00047     : AbstractTetrahedralElement<ELEMENT_DIM, SPACE_DIM>(index, rNodes)
00048 {
00049     if (registerWithNodes)
00050     {
00051         RegisterWithNodes();
00052     }
00053 }
00054 
00055 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00056 Element<ELEMENT_DIM, SPACE_DIM>::Element(const Element& rElement, const unsigned index)
00057 {
00058     *this = rElement;
00059     this->mIndex = index;
00060 
00061     RegisterWithNodes();
00062 }
00063 
00064 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00065 void Element<ELEMENT_DIM, SPACE_DIM>::RegisterWithNodes()
00066 {
00067     for (unsigned i=0; i<this->mNodes.size(); i++)
00068     {
00069         this->mNodes[i]->AddElement(this->mIndex);
00070     }
00071 }
00072 
00073 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00074 void Element<ELEMENT_DIM, SPACE_DIM>::MarkAsDeleted()
00075 {
00076     this->mIsDeleted = true;
00077     // Update nodes in this element so they know they are not contained by us
00078     for (unsigned i=0; i<this->GetNumNodes(); i++)
00079     {
00080         this->mNodes[i]->RemoveElement(this->mIndex);
00081     }
00082 }
00083 
00088 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00089 void Element<ELEMENT_DIM, SPACE_DIM>::UpdateNode(const unsigned& rIndex, Node<SPACE_DIM>* pNode)
00090 {
00091     assert(rIndex < this->mNodes.size());
00092 
00093     // Remove it from the node at this location
00094     this->mNodes[rIndex]->RemoveElement(this->mIndex);
00095 
00096     // Update the node at this location
00097     this->mNodes[rIndex] = pNode;
00098 
00099     // Add element to this node
00100     this->mNodes[rIndex]->AddElement(this->mIndex);
00101 }
00102 
00103 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00104 void Element<ELEMENT_DIM, SPACE_DIM>::ResetIndex(unsigned index)
00105 {
00106     //std::cout << "ResetIndex - removing nodes.\n" << std::flush;
00107     for (unsigned i=0; i<this->GetNumNodes(); i++)
00108     {
00109        //std::cout << "Node " << this->mNodes[i]->GetIndex() << " element "<< this->mIndex << std::flush;
00110        this->mNodes[i]->RemoveElement(this->mIndex);
00111     }
00112     //std::cout << "\nResetIndex - done.\n" << std::flush;
00113     this->mIndex=index;
00114     RegisterWithNodes();
00115 }
00116 
00117 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00118 c_vector<double,SPACE_DIM+1> Element<ELEMENT_DIM, SPACE_DIM>::CalculateCircumsphere(c_matrix<double, SPACE_DIM, ELEMENT_DIM>& rJacobian, c_matrix<double, ELEMENT_DIM, SPACE_DIM>& rInverseJacobian)
00119 {
00120     /*Assuming that x0,y0.. is at the origin then we need to solve
00121      *
00122      * ( 2x1 2y1 2z1  ) (x)    (x1^2+y1^2+z1^2)
00123      * ( 2x2 2y2 2z2  ) (y)    (x2^2+y2^2+z2^2)
00124      * ( 2x3 2y3 2z3  ) (z)    (x3^2+y3^2+z3^2)
00125      * where (x,y,z) is the circumcentre
00126      *
00127      */
00128 
00129     assert(ELEMENT_DIM == SPACE_DIM);
00130     c_vector<double, ELEMENT_DIM> rhs;
00131 
00132     for (unsigned j=0; j<ELEMENT_DIM; j++)
00133     {
00134         double squared_location=0.0;
00135         for (unsigned i=0; i<SPACE_DIM; i++)
00136         {
00137             //mJacobian(i,j) is the i-th component of j-th vertex (relative to vertex 0)
00138             squared_location += rJacobian(i,j)*rJacobian(i,j);
00139         }
00140         rhs[j]=squared_location/2.0;
00141     }
00142 
00143     c_vector<double, SPACE_DIM> centre;
00144     centre = prod(rhs, rInverseJacobian);
00145     c_vector<double, SPACE_DIM+1> circum;
00146     double squared_radius = 0.0;
00147     for (unsigned i=0; i<SPACE_DIM; i++)
00148     {
00149         circum[i] = centre[i] + this->GetNodeLocation(0,i);
00150         squared_radius += centre[i]*centre[i];
00151     }
00152     circum[SPACE_DIM] = squared_radius;
00153 
00154     return circum;
00155 }
00156 
00162 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00163 double Element<ELEMENT_DIM, SPACE_DIM>::CalculateQuality()
00164 {
00165     assert(SPACE_DIM == ELEMENT_DIM);
00166     if (SPACE_DIM == 1)
00167     {
00168         return 1.0;
00169     }
00170 
00171     c_matrix<double, SPACE_DIM, ELEMENT_DIM> jacobian;
00172     c_matrix<double, ELEMENT_DIM, SPACE_DIM> jacobian_inverse;
00173     double jacobian_determinant;
00174 
00175     this->CalculateInverseJacobian(jacobian, jacobian_determinant, jacobian_inverse);
00176 
00177     c_vector<double, SPACE_DIM+1> circum=CalculateCircumsphere(jacobian, jacobian_inverse);
00178     if (SPACE_DIM == 2)
00179     {
00180         /* Want Q=(Area_Tri / Area_Cir) / (Area_Equilateral_Tri / Area_Equilateral_Cir)
00181          * Area_Tri = |Jacobian| /2
00182          * Area_Cir = Pi * r^2
00183          * Area_Eq_Tri = (3*sqrt(3)/4)*R^2
00184          * Area_Eq_Tri = Pi * R^2
00185          * Q= (2*|Jacobian|)/3*sqrt(3)*r^2)
00186          */
00187         return 2.0*jacobian_determinant/(3.0*sqrt(3.0)*circum[SPACE_DIM]);
00188     }
00189     assert(SPACE_DIM == 3);
00190     /* Want Q=(Vol_Tet / Vol_CirS) / (Vol_Plat_Tet / Vol_Plat_CirS)
00191       *  Vol_Tet  = |Jacobian| /6
00192       *  Vol_CirS = 4*Pi*r^3/3
00193       *  Vol_Plat_Tet  = 8*sqrt(3)*R^3/27
00194       *  Vol_Plat_CirS = 4*Pi*R^3/3
00195      * Q= 3*sqrt(3)*|Jacobian|/ (16*r^3)
00196       */
00197 
00198     return (3.0*sqrt(3.0)*jacobian_determinant)
00199            /(16.0*circum[SPACE_DIM]*sqrt(circum[SPACE_DIM]));
00200 }
00201 
00202 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00203 c_vector <double, 2> Element<ELEMENT_DIM, SPACE_DIM>::CalculateMinMaxEdgeLengths()
00204 {
00205     c_vector <double, 2> min_max;
00206     min_max[0] = DBL_MAX; //Min initialised to very large
00207     min_max[1] = 0.0;     //Max initialised to zero
00208     for (unsigned i=0; i<=ELEMENT_DIM; i++)
00209     {
00210         c_vector<double, SPACE_DIM> loc_i = this->GetNodeLocation(i);
00211         for (unsigned j=i+1; j<=ELEMENT_DIM; j++)
00212         {
00213             double length = norm_2(this->GetNodeLocation(j) - loc_i);
00214             if (length < min_max[0])
00215             {
00216                 min_max[0] = length;
00217             }
00218             if (length > min_max[1])
00219             {
00220                 min_max[1] = length;
00221             }
00222         }
00223     }
00224     return min_max;
00225 }
00226 
00227 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00228 c_vector<double, SPACE_DIM+1> Element<ELEMENT_DIM, SPACE_DIM>::CalculateInterpolationWeights(const ChastePoint<SPACE_DIM>& rTestPoint)
00229 {
00230     //Can only test if it's a tetrahedal mesh in 3d, triangles in 2d...
00231     assert(ELEMENT_DIM == SPACE_DIM);
00232 
00233     c_vector<double, SPACE_DIM+1> weights;
00234 
00235     c_vector<double, SPACE_DIM> xi=CalculateXi(rTestPoint);
00236 
00237     //Copy 3 weights and compute the fourth weight
00238     weights[0]=1.0;
00239     for (unsigned i=1; i<=SPACE_DIM; i++)
00240     {
00241         weights[0] -= xi[i-1];
00242         weights[i] = xi[i-1];
00243     }
00244     return weights;
00245 }
00246 
00252 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00253 c_vector<double, SPACE_DIM+1> Element<ELEMENT_DIM, SPACE_DIM>::CalculateInterpolationWeightsWithProjection(const ChastePoint<SPACE_DIM>& rTestPoint)
00254 {
00255     //Can only test if it's a tetrahedal mesh in 3d, triangles in 2d...
00256     assert(ELEMENT_DIM == SPACE_DIM);
00257 
00258     c_vector<double, SPACE_DIM+1> weights = CalculateInterpolationWeights(rTestPoint);
00259 
00260     // Check for negative weights and set them to zero.
00261     bool negative_weight = false;
00262 
00263     for (unsigned i=0; i<=SPACE_DIM; i++)
00264     {
00265         if (weights[i] < 0.0)
00266         {
00267             weights[i] = 0.0;
00268 
00269             negative_weight = true;
00270         }
00271     }
00272 
00273     if (negative_weight == false)
00274     {
00275         // If there are no negative weights, there is nothing to do.
00276         return weights;
00277     }
00278 
00279     // Renormalise so that all weights add to 1.0.
00280 
00281     // Note that all elements of weights are now non-negative and so the l1-norm (sum of magnitudes) is equivalent to the sum of the elements of the vector
00282     double sum = norm_1 (weights);
00283 
00284     //l1-norm ought to be above 1 (because we scrubbed negative weights)
00285     //However, if we scrubbed weights that were the size of the machine precision then we might be close to one (even less than 1).
00286     assert( sum + DBL_EPSILON >= 1.0);
00287 
00288     //We might skip this division when sum ~= 1
00289     weights = weights/sum;
00290 
00291     return weights;
00292 }
00293 
00294 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00295 c_vector<double, SPACE_DIM> Element<ELEMENT_DIM, SPACE_DIM>::CalculateXi(const ChastePoint<SPACE_DIM>& rTestPoint)
00296 {
00297     //Can only test if it's a tetrahedal mesh in 3d, triangles in 2d...
00298     assert(ELEMENT_DIM == SPACE_DIM);
00299 
00300     // Find the location with respect to node 0
00303     ChastePoint<SPACE_DIM> copy = rTestPoint; // as rGetLocation in line below is not a const method
00304     c_vector<double, SPACE_DIM> test_location=copy.rGetLocation()-this->GetNodeLocation(0);
00305 
00306     //Multiply by inverse Jacobian
00307     c_matrix<double, SPACE_DIM, ELEMENT_DIM> jacobian;
00308     c_matrix<double, ELEMENT_DIM, SPACE_DIM> inverse_jacobian;
00309     double jacobian_determinant;
00310 
00312     this->CalculateInverseJacobian(jacobian, jacobian_determinant, inverse_jacobian);
00313 
00314     return prod(inverse_jacobian, test_location);
00315 }
00316 
00317 
00318 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
00319 bool Element<ELEMENT_DIM, SPACE_DIM>::IncludesPoint(const ChastePoint<SPACE_DIM>& rTestPoint, bool strict)
00320 {
00321     //Can only test if it's a tetrahedal mesh in 3d, triangles in 2d...
00322     assert(ELEMENT_DIM == SPACE_DIM);
00323 
00324     c_vector<double, SPACE_DIM+1> weights=CalculateInterpolationWeights(rTestPoint);
00325 
00326     //If the point is in the simplex then all the weights should be positive
00327 
00328     for (unsigned i=0; i<=SPACE_DIM; i++)
00329     {
00330         if (strict)
00331         {
00332             //Points can't be close to a face
00333             if (weights[i] <= 2*DBL_EPSILON)
00334             {
00335                 return false;
00336             }
00337         }
00338         else
00339         {
00340             //Allow point to be close to a face
00341             if (weights[i] < -2*DBL_EPSILON)
00342             {
00343                 return false;
00344             }
00345         }
00346     }
00347     return true;
00348 }
00349 
00351 // Explicit instantiation
00353 
00354 template class Element<1,1>;
00355 template class Element<1,2>;
00356 template class Element<1,3>;
00357 template class Element<2,2>;
00358 template class Element<2,3>;
00359 template class Element<3,3>;